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
19 * along with this program. If not, see <https://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 // m_netClasses maps name -> shared_ptr<NETCLASS>. The default pair operator==
389 // would compare the shared_ptrs by pointer identity, missing in-place edits
390 // to the underlying NETCLASS (the UI's normal edit path mutates through the
391 // shared_ptr rather than swapping it). Compare names and contents instead.
392 auto netclassEntryEqual = []( const auto& aLhs, const auto& aRhs )
393 {
394 if( aLhs.first != aRhs.first )
395 return false;
396
397 if( aLhs.second.get() == aRhs.second.get() )
398 return true;
399
400 if( !aLhs.second || !aRhs.second )
401 return false;
402
403 return aLhs.second->EqualsByPersistedFields( *aRhs.second );
404 };
405
406 if( !std::equal( std::begin( m_netClasses ), std::end( m_netClasses ),
407 std::begin( aOther.m_netClasses ), std::end( aOther.m_netClasses ),
408 netclassEntryEqual ) )
409 return false;
410
411 // m_defaultNetClass is held by shared_ptr and (per board.cpp / kicad_sexpr_parser.cpp
412 // setup paths) is mutated in place via GetDefaultNetclass()->SetClearance( ... ).
413 // shared_ptr identity is preserved across those edits, so a pointer-only check
414 // would silently drop the most common project edit. Compare contents.
415 if( static_cast<bool>( m_defaultNetClass ) != static_cast<bool>( aOther.m_defaultNetClass ) )
416 return false;
417
419 && !m_defaultNetClass->EqualsByPersistedFields( *aOther.m_defaultNetClass ) )
420 {
421 return false;
422 }
423
424 // m_netClassPatternAssignments stores std::unique_ptr<EDA_COMBINED_MATCHER>, so a naive
425 // std::equal would compare matcher pointer identity and report two settings instances built
426 // from identical input as unequal. Compare pattern text plus the assigned netclass name.
427 auto patternEqual = []( const auto& aLhs, const auto& aRhs )
428 {
429 if( !aLhs.first || !aRhs.first )
430 return aLhs.first.get() == aRhs.first.get() && aLhs.second == aRhs.second;
431
432 return aLhs.first->GetPattern() == aRhs.first->GetPattern() && aLhs.second == aRhs.second;
433 };
434
435 if( !std::equal( std::begin( m_netClassPatternAssignments ),
437 std::begin( aOther.m_netClassPatternAssignments ),
438 std::end( aOther.m_netClassPatternAssignments ),
439 patternEqual ) )
440 return false;
441
442 // m_netClassChainPatternAssignments is derived state, rebuilt from m_netChainClasses and
443 // board NETINFO on every netlist update. Equality is defined by persisted inputs only;
444 // including the derived list here would mark the project dirty whenever a rebuild produced
445 // a transient ordering difference.
446
447 if( !std::equal( std::begin( m_netClassLabelAssignments ),
448 std::end( m_netClassLabelAssignments ),
449 std::begin( aOther.m_netClassLabelAssignments ),
450 std::end( aOther.m_netClassLabelAssignments ) ) )
451 return false;
452
453 if( !std::equal( std::begin( m_netColorAssignments ), std::end( m_netColorAssignments ),
454 std::begin( aOther.m_netColorAssignments ),
455 std::end( aOther.m_netColorAssignments ) ) )
456 return false;
457
459 return false;
460
461 return true;
462}
463
464
466{
467 if( &aOther == this )
468 return;
469
470 // Flush the source's live field state into its JSON cache so the cache
471 // mirrors what we want to copy.
472 aOther.Store();
473
474 // CloneFrom() copies the JSON tree but leaves m_parent / m_path untouched,
475 // which is what we need: this instance must stay attached to its host
476 // project file so SaveToFile() walks the right m_nested_settings entry.
477 Internals()->CloneFrom( *aOther.Internals() );
478
479 // Repopulate our in-memory NETCLASS / pattern / color state from the cloned
480 // JSON. Load() rebuilds m_defaultNetClass, m_netClasses, fresh
481 // EDA_COMBINED_MATCHER instances for m_netClassPatternAssignments, and the
482 // remaining maps, decoupling our instance from aOther's lifetime.
483 Load();
484
485 // Load() repopulates persisted state but doesn't clear the derived caches.
486 // Drop them so stale lookups from this instance's pre-CopyFrom life
487 // (composite resolutions, implicit netclasses, chain-derived patterns
488 // rebuilt on every netlist update, and the per-net effective-class cache)
489 // don't survive the swap.
490 m_compositeNetClasses.clear();
491 m_impicitNetClasses.clear();
494}
495
496
498{
499 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
500 {
501 for( auto& netClass : m_internals->At( "classes" ).items() )
502 {
503 if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
504 {
505 nlohmann::json migrated = nlohmann::json::array();
506
507 for( auto& net : netClass.value()["nets"].items() )
508 migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
509
510 netClass.value()["nets"] = migrated;
511 }
512 }
513 }
514
515 return true;
516}
517
518
520{
521 return true;
522}
523
524
526{
527 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
528 {
529 nlohmann::json patterns = nlohmann::json::array();
530
531 for( auto& netClass : m_internals->At( "classes" ).items() )
532 {
533 if( netClass.value().contains( "name" )
534 && netClass.value().contains( "nets" )
535 && netClass.value()["nets"].is_array() )
536 {
537 wxString netClassName = netClass.value()["name"].get<wxString>();
538
539 for( auto& net : netClass.value()["nets"].items() )
540 {
541 nlohmann::json pattern_json = {
542 { "pattern", net.value().get<wxString>() },
543 { "netclass", netClassName }
544 };
545
546 patterns.push_back( pattern_json );
547 }
548 }
549 }
550
551 m_internals->SetFromString( "netclass_patterns", patterns );
552 }
553
554 return true;
555}
556
557
559{
560 // Add priority field to netclasses
561 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
562 {
563 int priority = 0;
564
565 for( auto& netClass : m_internals->At( "classes" ).items() )
566 {
567 if( netClass.value()["name"].get<wxString>() == NETCLASS::Default )
568 netClass.value()["priority"] = std::numeric_limits<int>::max();
569 else
570 netClass.value()["priority"] = priority++;
571 }
572 }
573
574 // Move netclass assignments to a list
575 if( m_internals->contains( "netclass_assignments" )
576 && m_internals->At( "netclass_assignments" ).is_object() )
577 {
578 nlohmann::json migrated = {};
579
580 for( const auto& pair : m_internals->At( "netclass_assignments" ).items() )
581 {
582 nlohmann::json netclassesJson = nlohmann::json::array();
583
584 if( pair.value().get<wxString>() != wxEmptyString )
585 netclassesJson.push_back( pair.value() );
586
587 migrated[pair.key()] = netclassesJson;
588 }
589
590 m_internals->SetFromString( "netclass_assignments", migrated );
591 }
592
593 return true;
594}
595
596
598{
599 // Add tuning profile name field to netclasses
600 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
601 {
602 const wxString emptyStr = "";
603
604 for( auto& netClass : m_internals->At( "classes" ).items() )
605 netClass.value()["tuning_profile"] = emptyStr.ToUTF8();
606 }
607
608 return true;
609}
610
611
612void NET_SETTINGS::SetDefaultNetclass( std::shared_ptr<NETCLASS> netclass )
613{
614 m_defaultNetClass = netclass;
615}
616
617
618std::shared_ptr<NETCLASS> NET_SETTINGS::GetDefaultNetclass() const
619{
620 return m_defaultNetClass;
621}
622
623
624bool NET_SETTINGS::HasNetclass( const wxString& netclassName ) const
625{
626 return m_netClasses.find( netclassName ) != m_netClasses.end();
627}
628
629
630void NET_SETTINGS::SetNetclass( const wxString& netclassName, std::shared_ptr<NETCLASS>& netclass )
631{
632 m_netClasses[netclassName] = netclass;
633}
634
635
636void NET_SETTINGS::SetNetclasses( const std::map<wxString, std::shared_ptr<NETCLASS>>& netclasses )
637{
638 m_netClasses = netclasses;
640}
641
642
643const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetNetclasses() const
644{
645 return m_netClasses;
646}
647
648
649const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetCompositeNetclasses() const
650{
652}
653
654
656{
657 m_netClasses.clear();
658 m_impicitNetClasses.clear();
660}
661
662
663const std::map<wxString, std::set<wxString>>& NET_SETTINGS::GetNetclassLabelAssignments() const
664{
666}
667
668
673
674
675void NET_SETTINGS::ClearNetclassLabelAssignment( const wxString& netName )
676{
677 m_netClassLabelAssignments.erase( netName );
678}
679
680
681void NET_SETTINGS::SetNetclassLabelAssignment( const wxString& netName,
682 const std::set<wxString>& netclasses )
683{
684 m_netClassLabelAssignments[netName] = netclasses;
685}
686
687
689 const std::set<wxString>& netclasses )
690{
691 m_netClassLabelAssignments[netName].insert( netclasses.begin(), netclasses.end() );
692}
693
694
695bool NET_SETTINGS::HasNetclassLabelAssignment( const wxString& netName ) const
696{
697 return m_netClassLabelAssignments.find( netName ) != m_netClassLabelAssignments.end();
698}
699
700
701void NET_SETTINGS::SetNetclassPatternAssignment( const wxString& pattern, const wxString& netclass )
702{
703 // Expand bus patterns (vector buses and bus groups) to individual member patterns.
704 // This is necessary because the regex/wildcard matchers interpret brackets and braces
705 // as special characters, not as bus notation.
706 ForEachBusMember( pattern,
707 [&]( const wxString& memberPattern )
708 {
709 addSinglePatternAssignment( memberPattern, netclass );
710 } );
711
713}
714
715
716void NET_SETTINGS::addSinglePatternAssignment( const wxString& pattern, const wxString& netclass )
717{
718 // Avoid exact duplicates - these shouldn't cause problems, due to later de-duplication
719 // but they are unnecessary.
720 for( auto& assignment : m_netClassPatternAssignments )
721 {
722 if( assignment.first->GetPattern() == pattern && assignment.second == netclass )
723 return;
724 }
725
726 // No assignment, add a new one
728 { std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ), netclass } );
729}
730
731
733 std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&& netclassPatterns )
734{
735 m_netClassPatternAssignments = std::move( netclassPatterns );
737}
738
739
740std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&
745
746
751
752
753void NET_SETTINGS::SetChainPatternAssignment( const wxString& pattern, const wxString& netclass )
754{
755 ForEachBusMember( pattern,
756 [&]( const wxString& memberPattern )
757 {
758 addSingleChainPatternAssignment( memberPattern, netclass );
759 } );
760
762}
763
764
766 const wxString& netclass )
767{
768 for( auto& assignment : m_netClassChainPatternAssignments )
769 {
770 if( !assignment.first )
771 continue;
772
773 if( assignment.first->GetPattern() == pattern && assignment.second == netclass )
774 return;
775 }
776
778 { std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ), netclass } );
779}
780
781
787
788
789void NET_SETTINGS::ClearCacheForNet( const wxString& netName )
790{
791 if( m_effectiveNetclassCache.count( netName ) )
792 {
793 wxString compositeNetclassName = m_effectiveNetclassCache[netName]->GetName();
794 m_compositeNetClasses.erase( compositeNetclassName );
795 m_effectiveNetclassCache.erase( netName );
796 }
797}
798
799
805
806
807void NET_SETTINGS::SetNetColorAssignment( const wxString& netName, const KIGFX::COLOR4D& color )
808{
809 m_netColorAssignments[netName] = color;
810}
811
812
813const std::map<wxString, KIGFX::COLOR4D>& NET_SETTINGS::GetNetColorAssignments() const
814{
816}
817
818
823
824
825bool NET_SETTINGS::HasEffectiveNetClass( const wxString& aNetName ) const
826{
827 return m_effectiveNetclassCache.count( aNetName ) > 0;
828}
829
830
831std::shared_ptr<NETCLASS> NET_SETTINGS::GetCachedEffectiveNetClass( const wxString& aNetName ) const
832{
833 return m_effectiveNetclassCache.at( aNetName );
834}
835
836
837std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aNetName )
838{
839 // Lambda to fetch an explicit netclass. Returns a nullptr if not found
840 auto getExplicitNetclass =
841 [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
842 {
843 if( netclass == NETCLASS::Default )
844 return m_defaultNetClass;
845
846 auto ii = m_netClasses.find( netclass );
847
848 if( ii == m_netClasses.end() )
849 return {};
850 else
851 return ii->second;
852 };
853
854 // Lambda to fetch or create an implicit netclass (defined with a label, but not configured)
855 // These are needed as while they do not provide any netclass parameters, they do now appear in
856 // DRC matching strings as an assigned netclass.
857 auto getOrAddImplicitNetcless =
858 [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
859 {
860 auto ii = m_impicitNetClasses.find( netclass );
861
862 if( ii == m_impicitNetClasses.end() )
863 {
864 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( netclass, false );
865 nc->SetPriority( std::numeric_limits<int>::max() - 1 ); // Priority > default netclass
866 m_impicitNetClasses[netclass] = nc;
867 return nc;
868 }
869 else
870 {
871 return ii->second;
872 }
873 };
874
875 // <no net> is forced to be part of the default netclass.
876 if( aNetName.IsEmpty() )
877 return m_defaultNetClass;
878
879 // First check if we have a cached resolved netclass
880 auto cacheItr = m_effectiveNetclassCache.find( aNetName );
881
882 if( cacheItr != m_effectiveNetclassCache.end() )
883 return cacheItr->second;
884
885 // No cache found - build a vector of all netclasses assigned to or matching this net
886 std::unordered_set<std::shared_ptr<NETCLASS>> resolvedNetclasses;
887
888 // First find explicit netclass assignments
889 auto it = m_netClassLabelAssignments.find( aNetName );
890
891 if( it != m_netClassLabelAssignments.end() && it->second.size() > 0 )
892 {
893 for( const wxString& netclassName : it->second )
894 {
895 std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
896
897 if( netclass )
898 {
899 resolvedNetclasses.insert( std::move( netclass ) );
900 }
901 else
902 {
903 resolvedNetclasses.insert( getOrAddImplicitNetcless( netclassName ) );
904 }
905 }
906 }
907
908 // Now find any pattern-matched netclass assignments (user + chain-derived)
909 auto applyPatternList =
910 [&]( const std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&
911 patterns )
912 {
913 for( const auto& [matcher, netclassName] : patterns )
914 {
915 if( matcher->StartsWith( aNetName ) )
916 {
917 std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
918
919 if( netclass )
920 resolvedNetclasses.insert( std::move( netclass ) );
921 else
922 resolvedNetclasses.insert( getOrAddImplicitNetcless( netclassName ) );
923 }
924 }
925 };
926
927 applyPatternList( m_netClassPatternAssignments );
928 applyPatternList( m_netClassChainPatternAssignments );
929
930 // Handle zero resolved netclasses
931 if( resolvedNetclasses.size() == 0 )
932 {
933 // For bus patterns, check if all members share the same netclass.
934 // If they do, the bus inherits that netclass for coloring purposes.
935 std::shared_ptr<NETCLASS> sharedNetclass;
936 bool allSameNetclass = true;
937 bool isBusPattern = false;
938
939 ForEachBusMember( aNetName,
940 [&]( const wxString& member )
941 {
942 // If ForEachBusMember gives us back the same name, it's not a bus.
943 // Skip to avoid infinite recursion.
944 if( member == aNetName )
945 return;
946
947 isBusPattern = true;
948
949 if( !allSameNetclass )
950 return;
951
952 std::shared_ptr<NETCLASS> memberNc = GetEffectiveNetClass( member );
953
954 if( !sharedNetclass )
955 {
956 sharedNetclass = memberNc;
957 }
958 else if( memberNc->GetName() != sharedNetclass->GetName() )
959 {
960 allSameNetclass = false;
961 }
962 } );
963
964 if( isBusPattern && allSameNetclass && sharedNetclass
965 && sharedNetclass->GetName() != NETCLASS::Default )
966 {
967 m_effectiveNetclassCache[aNetName] = sharedNetclass;
968 return sharedNetclass;
969 }
970
972
973 return m_defaultNetClass;
974 }
975
976 // Make and cache the effective netclass. Note that makeEffectiveNetclass will add the default
977 // netclass to resolvedNetclasses if it is needed to complete the netclass paramters set. It
978 // will also sort resolvedNetclasses by priority order.
979 std::vector<NETCLASS*> netclassPtrs;
980
981 for( const std::shared_ptr<NETCLASS>& nc : resolvedNetclasses )
982 netclassPtrs.push_back( nc.get() );
983
984 wxString name;
985 name.Printf( "Effective for net: %s", aNetName );
986 std::shared_ptr<NETCLASS> effectiveNetclass = std::make_shared<NETCLASS>( name, false );
987 makeEffectiveNetclass( effectiveNetclass, netclassPtrs );
988
989 if( netclassPtrs.size() == 1 )
990 {
991 // No defaults were added - just return the primary netclass
992 m_effectiveNetclassCache[aNetName] = *resolvedNetclasses.begin();
993 return *resolvedNetclasses.begin();
994 }
995 else
996 {
997 effectiveNetclass->SetConstituentNetclasses( std::move( netclassPtrs ) );
998
999 m_compositeNetClasses[effectiveNetclass->GetName()] = effectiveNetclass;
1000 m_effectiveNetclassCache[aNetName] = effectiveNetclass;
1001
1002 return effectiveNetclass;
1003 }
1004}
1005
1006
1008{
1009 for( auto& [ncName, nc] : m_compositeNetClasses )
1010 {
1011 // Note this needs to be a copy in case we now need to add the default netclass
1012 std::vector<NETCLASS*> constituents = nc->GetConstituentNetclasses();
1013
1014 wxASSERT( constituents.size() > 0 );
1015
1016 // If the last netclass is Default, remove it (it will be re-added if still needed)
1017 if( ( *constituents.rbegin() )->GetName() == NETCLASS::Default )
1018 {
1019 constituents.pop_back();
1020 }
1021
1022 // Remake the netclass from original constituents
1023 nc->ResetParameters();
1024 makeEffectiveNetclass( nc, constituents );
1025 nc->SetConstituentNetclasses( std::move( constituents ) );
1026 }
1027}
1028
1029
1030void NET_SETTINGS::makeEffectiveNetclass( std::shared_ptr<NETCLASS>& effectiveNetclass,
1031 std::vector<NETCLASS*>& constituentNetclasses ) const
1032{
1033 // Sort the resolved netclasses by priority (highest first), with same-priority netclasses
1034 // ordered alphabetically
1035 std::sort( constituentNetclasses.begin(), constituentNetclasses.end(),
1036 []( NETCLASS* nc1, NETCLASS* nc2 )
1037 {
1038 int p1 = nc1->GetPriority();
1039 int p2 = nc2->GetPriority();
1040
1041 if( p1 < p2 )
1042 return true;
1043
1044 if (p1 == p2)
1045 return nc1->GetName().Cmp( nc2->GetName() ) < 0;
1046
1047 return false;
1048 } );
1049
1050 // Iterate from lowest priority netclass and fill effective netclass parameters
1051 for( auto itr = constituentNetclasses.rbegin(); itr != constituentNetclasses.rend(); ++itr )
1052 {
1053 NETCLASS* nc = *itr;
1054
1055 if( nc->HasClearance() )
1056 {
1057 effectiveNetclass->SetClearance( nc->GetClearance() );
1058 effectiveNetclass->SetClearanceParent( nc );
1059 }
1060
1061 if( nc->HasTrackWidth() )
1062 {
1063 effectiveNetclass->SetTrackWidth( nc->GetTrackWidth() );
1064 effectiveNetclass->SetTrackWidthParent( nc );
1065 }
1066
1067 if( nc->HasViaDiameter() )
1068 {
1069 effectiveNetclass->SetViaDiameter( nc->GetViaDiameter() );
1070 effectiveNetclass->SetViaDiameterParent( nc );
1071 }
1072
1073 if( nc->HasViaDrill() )
1074 {
1075 effectiveNetclass->SetViaDrill( nc->GetViaDrill() );
1076 effectiveNetclass->SetViaDrillParent( nc );
1077 }
1078
1079 if( nc->HasuViaDiameter() )
1080 {
1081 effectiveNetclass->SetuViaDiameter( nc->GetuViaDiameter() );
1082 effectiveNetclass->SetuViaDiameterParent( nc );
1083 }
1084
1085 if( nc->HasuViaDrill() )
1086 {
1087 effectiveNetclass->SetuViaDrill( nc->GetuViaDrill() );
1088 effectiveNetclass->SetuViaDrillParent( nc );
1089 }
1090
1091 if( nc->HasDiffPairWidth() )
1092 {
1093 effectiveNetclass->SetDiffPairWidth( nc->GetDiffPairWidth() );
1094 effectiveNetclass->SetDiffPairWidthParent( nc );
1095 }
1096
1097 if( nc->HasDiffPairGap() )
1098 {
1099 effectiveNetclass->SetDiffPairGap( nc->GetDiffPairGap() );
1100 effectiveNetclass->SetDiffPairGapParent( nc );
1101 }
1102
1103 if( nc->HasDiffPairViaGap() )
1104 {
1105 effectiveNetclass->SetDiffPairViaGap( nc->GetDiffPairViaGap() );
1106 effectiveNetclass->SetDiffPairViaGapParent( nc );
1107 }
1108
1109 if( nc->HasWireWidth() )
1110 {
1111 effectiveNetclass->SetWireWidth( nc->GetWireWidth() );
1112 effectiveNetclass->SetWireWidthParent( nc );
1113 }
1114
1115 if( nc->HasBusWidth() )
1116 {
1117 effectiveNetclass->SetBusWidth( nc->GetBusWidth() );
1118 effectiveNetclass->SetBusWidthParent( nc );
1119 }
1120
1121 if( nc->HasLineStyle() )
1122 {
1123 effectiveNetclass->SetLineStyle( nc->GetLineStyle() );
1124 effectiveNetclass->SetLineStyleParent( nc );
1125 }
1126
1127 COLOR4D pcbColor = nc->GetPcbColor();
1128
1129 if( pcbColor != COLOR4D::UNSPECIFIED )
1130 {
1131 effectiveNetclass->SetPcbColor( pcbColor );
1132 effectiveNetclass->SetPcbColorParent( nc );
1133 }
1134
1135 COLOR4D schColor = nc->GetSchematicColor();
1136
1137 if( schColor != COLOR4D::UNSPECIFIED )
1138 {
1139 effectiveNetclass->SetSchematicColor( schColor );
1140 effectiveNetclass->SetSchematicColorParent( nc );
1141 }
1142
1143 if( nc->HasTuningProfile() )
1144 {
1145 effectiveNetclass->SetTuningProfile( nc->GetTuningProfile() );
1146 effectiveNetclass->SetTuningProfileParent( nc );
1147 }
1148 }
1149
1150 // Fill in any required defaults
1151 if( addMissingDefaults( effectiveNetclass.get() ) )
1152 constituentNetclasses.push_back( m_defaultNetClass.get() );
1153}
1154
1155
1157{
1158 bool addedDefault = false;
1159
1160 if( !nc->HasClearance() )
1161 {
1162 addedDefault = true;
1163 nc->SetClearance( m_defaultNetClass->GetClearance() );
1165 }
1166
1167 if( !nc->HasTrackWidth() )
1168 {
1169 addedDefault = true;
1170 nc->SetTrackWidth( m_defaultNetClass->GetTrackWidth() );
1172 }
1173
1174 if( !nc->HasViaDiameter() )
1175 {
1176 addedDefault = true;
1177 nc->SetViaDiameter( m_defaultNetClass->GetViaDiameter() );
1179 }
1180
1181 if( !nc->HasViaDrill() )
1182 {
1183 addedDefault = true;
1184 nc->SetViaDrill( m_defaultNetClass->GetViaDrill() );
1186 }
1187
1188 if( !nc->HasuViaDiameter() )
1189 {
1190 addedDefault = true;
1191 nc->SetuViaDiameter( m_defaultNetClass->GetuViaDiameter() );
1193 }
1194
1195 if( !nc->HasuViaDrill() )
1196 {
1197 addedDefault = true;
1198 nc->SetuViaDrill( m_defaultNetClass->GetuViaDrill() );
1200 }
1201
1202 if( !nc->HasDiffPairWidth() )
1203 {
1204 addedDefault = true;
1205 nc->SetDiffPairWidth( m_defaultNetClass->GetDiffPairWidth() );
1207 }
1208
1209 if( !nc->HasDiffPairGap() )
1210 {
1211 addedDefault = true;
1212 nc->SetDiffPairGap( m_defaultNetClass->GetDiffPairGap() );
1214 }
1215
1216 // Currently this is only on the default netclass, and not editable in the setup panel
1217 // if( !nc->HasDiffPairViaGap() )
1218 // {
1219 // addedDefault = true;
1220 // nc->SetDiffPairViaGap( m_defaultNetClass->GetDiffPairViaGap() );
1221 // nc->SetDiffPairViaGapParent( m_defaultNetClass.get() );
1222 // }
1223
1224 if( !nc->HasWireWidth() )
1225 {
1226 addedDefault = true;
1227 nc->SetWireWidth( m_defaultNetClass->GetWireWidth() );
1229 }
1230
1231 if( !nc->HasBusWidth() )
1232 {
1233 addedDefault = true;
1234 nc->SetBusWidth( m_defaultNetClass->GetBusWidth() );
1236 }
1237
1238 // The tuning profile can be empty - only fill if a default tuning profile is set
1239 if( !nc->HasTuningProfile() && m_defaultNetClass->HasTuningProfile() )
1240 {
1241 addedDefault = true;
1242 nc->SetTuningProfile( m_defaultNetClass->GetTuningProfile() );
1244 }
1245
1246 return addedDefault;
1247}
1248
1249
1250std::shared_ptr<NETCLASS> NET_SETTINGS::GetNetClassByName( const wxString& aNetClassName ) const
1251{
1252 auto ii = m_netClasses.find( aNetClassName );
1253
1254 if( ii == m_netClasses.end() )
1255 return m_defaultNetClass;
1256 else
1257 return ii->second;
1258}
1259
1260
1261static bool isSuperSubOverbar( wxChar c )
1262{
1263 return c == '_' || c == '^' || c == '~';
1264}
1265
1266
1274static bool isEscaped( const wxString& aStr, size_t aPos )
1275{
1276 if( aPos == 0 )
1277 return false;
1278
1279 // Count consecutive backslashes before this position
1280 int backslashCount = 0;
1281 size_t pos = aPos;
1282
1283 while( pos > 0 && aStr[pos - 1] == '\\' )
1284 {
1285 backslashCount++;
1286 pos--;
1287 }
1288
1289 // If odd number of backslashes, the character is escaped
1290 return ( backslashCount % 2 ) == 1;
1291}
1292
1293
1294bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
1295 std::vector<wxString>* aMemberList )
1296{
1297 auto isDigit =
1298 []( wxChar c )
1299 {
1300 static wxString digits( wxT( "0123456789" ) );
1301 return digits.Contains( c );
1302 };
1303
1304 size_t busLen = aBus.length();
1305 size_t i = 0;
1306 wxString prefix;
1307 wxString suffix;
1308 wxString tmp;
1309 long begin = 0;
1310 long end = 0;
1311 int braceNesting = 0;
1312 bool fmtWrapsName = false;
1313 bool inQuotes = false;
1314
1315 prefix.reserve( busLen );
1316
1317 // Parse prefix
1318 //
1319 // Formatting markers (^{}, _{}, ~{}) can appear either as part of the prefix name
1320 // (e.g. I^{2}C[0..7]) or wrapping the range specifier (e.g. D_{[1..2]}).
1321 // We preserve formatting in the prefix and only strip it when the range bracket
1322 // appears inside formatting braces, indicating the formatting wraps the range.
1323 //
1324 for( ; i < busLen; ++i )
1325 {
1326 // Handle quoted strings (allows spaces inside)
1327 if( aBus[i] == '"' && !isEscaped( aBus, i ) )
1328 {
1329 inQuotes = !inQuotes;
1330 continue;
1331 }
1332
1333 if( inQuotes )
1334 {
1335 // Inside quotes, add characters directly (including spaces)
1336 if( aBus[i] == '\\' && i + 1 < busLen )
1337 {
1338 // Handle escaped characters inside quotes
1339 prefix += aBus[++i];
1340 }
1341 else
1342 {
1343 prefix += aBus[i];
1344 }
1345
1346 continue;
1347 }
1348
1349 if( aBus[i] == '{' )
1350 {
1351 if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
1352 {
1353 braceNesting++;
1354 prefix += wxT( '{' );
1355 continue;
1356 }
1357 else
1358 return false;
1359 }
1360 else if( aBus[i] == '}' )
1361 {
1362 braceNesting--;
1363 prefix += wxT( '}' );
1364 continue;
1365 }
1366
1367 // Handle backslash-escaped spaces
1368 if( aBus[i] == '\\' && i + 1 < busLen && aBus[i + 1] == ' ' )
1369 {
1370 prefix += aBus[++i];
1371 continue;
1372 }
1373
1374 // Unescaped space or ] in bus vector prefix is not allowed
1375 if( aBus[i] == ' ' || aBus[i] == ']' )
1376 return false;
1377
1378 if( aBus[i] == '[' )
1379 {
1380 if( braceNesting > 0 )
1381 {
1382 size_t fmtStart = prefix.rfind( wxT( '{' ) );
1383
1384 if( fmtStart != wxString::npos && fmtStart > 0
1385 && isSuperSubOverbar( prefix[fmtStart - 1] ) )
1386 {
1387 if( fmtStart == prefix.length() - 1 )
1388 {
1389 // '{' immediately precedes '[' (e.g. D_{[1..2]}).
1390 // The formatting decorates the range indices, not the
1391 // name itself.
1392 prefix.erase( fmtStart - 1 );
1393 }
1394 else
1395 {
1396 // Name characters exist between '{' and '[' (e.g.
1397 // ~{BE[0..3]}). The formatting wraps the signal name,
1398 // not the range.
1399 fmtWrapsName = true;
1400 }
1401 }
1402 }
1403
1404 break;
1405 }
1406
1407 prefix += aBus[i];
1408 }
1409
1410 // Parse start number
1411 //
1412 i++; // '[' character
1413
1414 if( i >= busLen )
1415 return false;
1416
1417 for( ; i < busLen; ++i )
1418 {
1419 if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
1420 {
1421 tmp.ToLong( &begin );
1422 i += 2;
1423 break;
1424 }
1425
1426 if( !isDigit( aBus[i] ) )
1427 return false;
1428
1429 tmp += aBus[i];
1430 }
1431
1432 // Parse end number
1433 //
1434 tmp = wxEmptyString;
1435
1436 if( i >= busLen )
1437 return false;
1438
1439 for( ; i < busLen; ++i )
1440 {
1441 if( aBus[i] == ']' )
1442 {
1443 tmp.ToLong( &end );
1444 ++i;
1445 break;
1446 }
1447
1448 if( !isDigit( aBus[i] ) )
1449 return false;
1450
1451 tmp += aBus[i];
1452 }
1453
1454 // Parse suffix
1455 //
1456 for( ; i < busLen; ++i )
1457 {
1458 if( aBus[i] == '}' )
1459 {
1460 braceNesting--;
1461
1462 if( fmtWrapsName )
1463 suffix += aBus[i];
1464 }
1465 else if( aBus[i] == '+' || aBus[i] == '-' || aBus[i] == 'P' || aBus[i] == 'N' )
1466 {
1467 suffix += aBus[i];
1468 }
1469 else
1470 {
1471 return false;
1472 }
1473 }
1474
1475 if( braceNesting != 0 )
1476 return false;
1477
1478 if( begin == end )
1479 return false;
1480 else if( begin > end )
1481 std::swap( begin, end );
1482
1483 if( aName )
1484 *aName = prefix;
1485
1486 if( aMemberList )
1487 {
1488 for( long idx = begin; idx <= end; ++idx )
1489 {
1490 wxString str = prefix;
1491 str << idx;
1492 str << suffix;
1493
1494 aMemberList->emplace_back( str );
1495 }
1496 }
1497
1498 return true;
1499}
1500
1501
1502bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
1503 std::vector<wxString>* aMemberList )
1504{
1505 size_t groupLen = aGroup.length();
1506 size_t i = 0;
1507 wxString prefix;
1508 wxString tmp;
1509 int braceNesting = 0;
1510 bool inQuotes = false;
1511
1512 prefix.reserve( groupLen );
1513
1514 // Escape spaces in member names so recursive parsing by ForEachBusMember works correctly.
1515 // Both quoted strings and backslash-escaped spaces collapse to bare spaces during parsing,
1516 // so we must re-escape them for subsequent ParseBusVector/ParseBusGroup calls.
1517 auto escapeSpacesForBus =
1518 []( const wxString& aMember ) -> wxString
1519 {
1520 wxString escaped;
1521 escaped.reserve( aMember.length() * 2 );
1522
1523 for( wxUniChar c : aMember )
1524 {
1525 if( c == ' ' )
1526 escaped += wxT( "\\ " );
1527 else
1528 escaped += c;
1529 }
1530
1531 return escaped;
1532 };
1533
1534 // Parse prefix
1535 //
1536 // Formatting markers (^{}, _{}, ~{}) in the prefix are part of the group name
1537 // and must be preserved. The member-list opening brace is distinguished by NOT
1538 // being preceded by a formatting character.
1539 //
1540 for( ; i < groupLen; ++i )
1541 {
1542 // Handle quoted strings (allows spaces inside)
1543 if( aGroup[i] == '"' && !isEscaped( aGroup, i ) )
1544 {
1545 inQuotes = !inQuotes;
1546 continue;
1547 }
1548
1549 if( inQuotes )
1550 {
1551 // Inside quotes, add characters directly (including spaces)
1552 if( aGroup[i] == '\\' && i + 1 < groupLen )
1553 {
1554 // Handle escaped characters inside quotes
1555 prefix += aGroup[++i];
1556 }
1557 else
1558 {
1559 prefix += aGroup[i];
1560 }
1561
1562 continue;
1563 }
1564
1565 if( aGroup[i] == '{' )
1566 {
1567 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
1568 {
1569 braceNesting++;
1570 prefix += wxT( '{' );
1571 continue;
1572 }
1573 else
1574 break;
1575 }
1576 else if( aGroup[i] == '}' )
1577 {
1578 braceNesting--;
1579 prefix += wxT( '}' );
1580 continue;
1581 }
1582
1583 // Handle backslash-escaped spaces
1584 if( aGroup[i] == '\\' && i + 1 < groupLen && aGroup[i + 1] == ' ' )
1585 {
1586 prefix += aGroup[++i];
1587 continue;
1588 }
1589
1590 // Unescaped space, [, or ] in bus group prefix is not allowed
1591 if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
1592 return false;
1593
1594 prefix += aGroup[i];
1595 }
1596
1597 if( braceNesting != 0 )
1598 return false;
1599
1600 if( aName )
1601 *aName = prefix;
1602
1603 // Parse members
1604 //
1605 i++; // '{' character
1606
1607 if( i >= groupLen )
1608 return false;
1609
1610 inQuotes = false;
1611
1612 for( ; i < groupLen; ++i )
1613 {
1614 // Handle quoted strings (allows spaces inside member names)
1615 if( aGroup[i] == '"' && !isEscaped( aGroup, i ) )
1616 {
1617 inQuotes = !inQuotes;
1618 continue;
1619 }
1620
1621 if( inQuotes )
1622 {
1623 // Inside quotes, add characters directly (including spaces)
1624 if( aGroup[i] == '\\' && i + 1 < groupLen )
1625 {
1626 // Handle escaped characters inside quotes
1627 tmp += aGroup[++i];
1628 }
1629 else
1630 {
1631 tmp += aGroup[i];
1632 }
1633
1634 continue;
1635 }
1636
1637 if( aGroup[i] == '{' )
1638 {
1639 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
1640 {
1641 braceNesting++;
1642
1643 // Keep the full formatting notation (e.g. ~{CAS}) in the member name.
1644 // A net named ~{CAS} is distinct from CAS, and stripping the marker
1645 // would lose that identity. Vector bus members like D_{[1..2]} also
1646 // preserve their subscript so recursive ForEachBusMember can parse them.
1647 tmp += wxT( '{' );
1648 continue;
1649 }
1650 else
1651 return false;
1652 }
1653 else if( aGroup[i] == '}' )
1654 {
1655 if( braceNesting )
1656 {
1657 braceNesting--;
1658 tmp += wxT( '}' );
1659 continue;
1660 }
1661 else
1662 {
1663 if( aMemberList && !tmp.IsEmpty() )
1664 aMemberList->push_back( EscapeString( escapeSpacesForBus( tmp ), CTX_NETNAME ) );
1665
1666 return true;
1667 }
1668 }
1669
1670 // Handle backslash-escaped spaces in member names
1671 if( aGroup[i] == '\\' && i + 1 < groupLen && aGroup[i + 1] == ' ' )
1672 {
1673 tmp += aGroup[++i];
1674 continue;
1675 }
1676
1677 // Unescaped space or comma separates members
1678 if( aGroup[i] == ' ' || aGroup[i] == ',' )
1679 {
1680 if( aMemberList && !tmp.IsEmpty() )
1681 aMemberList->push_back( EscapeString( escapeSpacesForBus( tmp ), CTX_NETNAME ) );
1682
1683 tmp.Clear();
1684 continue;
1685 }
1686
1687 tmp += aGroup[i];
1688 }
1689
1690 return false;
1691}
1692
1693
1694void NET_SETTINGS::ForEachBusMember( const wxString& aBusPattern,
1695 const std::function<void( const wxString& )>& aFunction )
1696{
1697 std::vector<wxString> members;
1698
1699 if( ParseBusVector( aBusPattern, nullptr, &members ) )
1700 {
1701 // Vector bus: call function for each expanded member
1702 for( const wxString& member : members )
1703 aFunction( member );
1704 }
1705 else if( ParseBusGroup( aBusPattern, nullptr, &members ) )
1706 {
1707 // Bus group: recursively expand each member (which may itself be a vector or group)
1708 for( const wxString& member : members )
1709 ForEachBusMember( member, aFunction );
1710 }
1711 else
1712 {
1713 // Not a bus pattern: call function with the original pattern
1714 aFunction( aBusPattern );
1715 }
1716}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
void CloneFrom(const JSON_SETTINGS_INTERNALS &aOther)
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
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_INTERNALS * Internals()
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
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:38
void SetViaDiameter(int aDia)
Definition netclass.h:141
void SetViaDrill(int aSize)
Definition netclass.h:149
bool HasLineStyle() const
Definition netclass.h:239
int GetViaDiameter() const
Definition netclass.h:139
int GetViaDrill() const
Definition netclass.h:147
void SetWireWidthParent(NETCLASS *parent)
Definition netclass.h:214
static const char Default[]
the name of the default NETCLASS
Definition netclass.h:40
void SetuViaDrillParent(NETCLASS *parent)
Definition netclass.h:167
bool HasBusWidth() const
Definition netclass.h:217
bool HasuViaDrill() const
Definition netclass.h:162
void SetDiffPairWidthParent(NETCLASS *parent)
Definition netclass.h:175
void SetuViaDiameter(int aSize)
Definition netclass.h:157
void SetDiffPairWidth(int aSize)
Definition netclass.h:173
int HasViaDrill() const
Definition netclass.h:146
int GetDiffPairViaGap() const
Definition netclass.h:187
void SetViaDrillParent(NETCLASS *parent)
Definition netclass.h:151
wxString GetTuningProfile() const
Definition netclass.h:252
void SetDiffPairGapParent(NETCLASS *parent)
Definition netclass.h:183
void SetTuningProfileParent(NETCLASS *aParent)
Definition netclass.h:253
int GetDiffPairGap() const
Definition netclass.h:179
int GetuViaDrill() const
Definition netclass.h:163
bool HasViaDiameter() const
Definition netclass.h:138
int GetLineStyle() const
Definition netclass.h:240
bool HasDiffPairWidth() const
Definition netclass.h:170
bool HasuViaDiameter() const
Definition netclass.h:154
void SetTrackWidthParent(NETCLASS *parent)
Definition netclass.h:135
int GetuViaDiameter() const
Definition netclass.h:155
bool HasTrackWidth() const
Definition netclass.h:130
void SetViaDiameterParent(NETCLASS *parent)
Definition netclass.h:143
int GetDiffPairWidth() const
Definition netclass.h:171
void SetuViaDrill(int aSize)
Definition netclass.h:165
int GetWireWidth() const
Definition netclass.h:210
void SetDiffPairGap(int aSize)
Definition netclass.h:181
void SetBusWidthParent(NETCLASS *parent)
Definition netclass.h:222
void SetClearance(int aClearance)
Definition netclass.h:125
COLOR4D GetPcbColor(bool aIsForSave=false) const
Definition netclass.h:195
bool HasDiffPairGap() const
Definition netclass.h:178
COLOR4D GetSchematicColor(bool aIsForSave=false) const
Definition netclass.h:225
void SetBusWidth(int aWidth)
Definition netclass.h:220
void SetClearanceParent(NETCLASS *parent)
Definition netclass.h:127
int GetTrackWidth() const
Definition netclass.h:131
void SetWireWidth(int aWidth)
Definition netclass.h:212
void SetTuningProfile(const wxString &aTuningProfile)
Definition netclass.h:251
bool HasTuningProfile() const
Definition netclass.h:250
bool HasWireWidth() const
Definition netclass.h:209
int GetClearance() const
Definition netclass.h:123
void SetuViaDiameterParent(NETCLASS *parent)
Definition netclass.h:159
void SetTrackWidth(int aWidth)
Definition netclass.h:133
bool HasDiffPairViaGap() const
Definition netclass.h:186
int GetBusWidth() const
Definition netclass.h:218
bool HasClearance() const
Definition netclass.h:122
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() const
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()
void CopyFrom(NET_SETTINGS &aOther)
Deep-copy the persisted contents of aOther into this instance.
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:465
#define _(s)
@ CTX_NETCLASS
nlohmann::json json
Definition gerbview.cpp:49
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