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 // Let the save drop removed colors instead of merging them back in
253 m_params.back()->SetClearUnknownKeys();
254
255 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_chain_classes",
256 [&]() -> nlohmann::json
257 {
258 // Force object type so an empty map round-trips as {} rather than null;
259 // the reader rejects non-objects, which would otherwise leave stale
260 // chain assignments in place after the user clears them all.
261 nlohmann::json ret = nlohmann::json::object();
262
263 for( const auto& [chain, className] : m_netChainClasses )
264 ret[ std::string( chain.ToUTF8() ) ] = std::string( className.ToUTF8() );
265
266 return ret;
267 },
268 [&]( const nlohmann::json& aJson )
269 {
270 if( !aJson.is_object() )
271 return;
272
273 m_netChainClasses.clear();
274
275 for( const auto& pair : aJson.items() )
276 {
277 wxString chain( pair.key().c_str(), wxConvUTF8 );
278 wxString className = pair.value().get<wxString>();
279
280 if( !className.IsEmpty() )
281 m_netChainClasses[ std::move( chain ) ] = std::move( className );
282 }
283 },
284 {} ) );
285
286 // Let the save drop removed chains instead of merging them back in
287 m_params.back()->SetClearUnknownKeys();
288
289 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "net_chain_netclasses",
290 [&]() -> nlohmann::json
291 {
292 // Force object type so an empty map round-trips as {} rather than null;
293 // the reader rejects non-objects, which would otherwise leave stale
294 // chain assignments in place after the user clears them all.
295 nlohmann::json ret = nlohmann::json::object();
296
297 for( const auto& [chain, netclass] : m_netChainNetClasses )
298 ret[ std::string( chain.ToUTF8() ) ] = std::string( netclass.ToUTF8() );
299
300 return ret;
301 },
302 [&]( const nlohmann::json& aJson )
303 {
304 if( !aJson.is_object() )
305 return;
306
307 m_netChainNetClasses.clear();
308
309 for( const auto& pair : aJson.items() )
310 {
311 wxString chain( pair.key().c_str(), wxConvUTF8 );
312 wxString netclass = pair.value().get<wxString>();
313
314 if( !netclass.IsEmpty() )
315 m_netChainNetClasses[ std::move( chain ) ] = std::move( netclass );
316 }
317 },
318 {} ) );
319
320 // Let the save drop removed chain netclasses instead of merging them back in
321 m_params.back()->SetClearUnknownKeys();
322
323 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_assignments",
324 [&]() -> nlohmann::json
325 {
326 nlohmann::json ret = {};
327
328 for( const auto& [netname, netclassNames] : m_netClassLabelAssignments )
329 {
330 nlohmann::json netclassesJson = nlohmann::json::array();
331
332 for( const auto& netclass : netclassNames )
333 {
334 std::string netclassStr( netclass.ToUTF8() );
335 netclassesJson.push_back( std::move( netclassStr ) );
336 }
337
338 std::string key( netname.ToUTF8() );
339 ret[std::move( key )] = netclassesJson;
340 }
341
342 return ret;
343 },
344 [&]( const nlohmann::json& aJson )
345 {
346 if( !aJson.is_object() )
347 return;
348
350
351 for( const auto& pair : aJson.items() )
352 {
353 wxString key( pair.key().c_str(), wxConvUTF8 );
354
355 for( const auto& netclassName : pair.value() )
356 m_netClassLabelAssignments[key].insert( netclassName.get<wxString>() );
357 }
358 },
359 {} ) );
360
361 m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "netclass_patterns",
362 [&]() -> nlohmann::json
363 {
364 nlohmann::json ret = nlohmann::json::array();
365
366 for( const auto& [matcher, netclassName] : m_netClassPatternAssignments )
367 {
368 nlohmann::json pattern_json = {
369 { "pattern", matcher->GetPattern().ToUTF8() },
370 { "netclass", netclassName.ToUTF8() }
371 };
372
373 ret.push_back( std::move( pattern_json ) );
374 }
375
376 return ret;
377 },
378 [&]( const nlohmann::json& aJson )
379 {
380 if( !aJson.is_array() )
381 return;
382
384
385 for( const nlohmann::json& entry : aJson )
386 {
387 if( !entry.is_object() )
388 continue;
389
390 if( entry.contains( "pattern" ) && entry["pattern"].is_string()
391 && entry.contains( "netclass" ) && entry["netclass"].is_string() )
392 {
393 wxString pattern = entry["pattern"].get<wxString>();
394 wxString netclass = entry["netclass"].get<wxString>();
395
396 // Expand bus patterns so individual bus member nets can be matched
397 ForEachBusMember( pattern,
398 [&]( const wxString& memberPattern )
399 {
400 addSinglePatternAssignment( memberPattern, netclass );
401 } );
402 }
403 }
404 },
405 {} ) );
406
407 registerMigration( 0, 1, std::bind( &NET_SETTINGS::migrateSchema0to1, this ) );
408 registerMigration( 1, 2, std::bind( &NET_SETTINGS::migrateSchema1to2, this ) );
409 registerMigration( 2, 3, std::bind( &NET_SETTINGS::migrateSchema2to3, this ) );
410 registerMigration( 3, 4, std::bind( &NET_SETTINGS::migrateSchema3to4, this ) );
411 registerMigration( 4, 5, std::bind( &NET_SETTINGS::migrateSchema4to5, this ) );
412}
413
414
416{
417 // Release early before destroying members
418 if( m_parent )
419 {
420 m_parent->ReleaseNestedSettings( this );
421 m_parent = nullptr;
422 }
423}
424
425
426bool NET_SETTINGS::operator==( const NET_SETTINGS& aOther ) const
427{
428 // m_netClasses maps name -> shared_ptr<NETCLASS>. The default pair operator==
429 // would compare the shared_ptrs by pointer identity, missing in-place edits
430 // to the underlying NETCLASS (the UI's normal edit path mutates through the
431 // shared_ptr rather than swapping it). Compare names and contents instead.
432 auto netclassEntryEqual = []( const auto& aLhs, const auto& aRhs )
433 {
434 if( aLhs.first != aRhs.first )
435 return false;
436
437 if( aLhs.second.get() == aRhs.second.get() )
438 return true;
439
440 if( !aLhs.second || !aRhs.second )
441 return false;
442
443 return aLhs.second->EqualsByPersistedFields( *aRhs.second );
444 };
445
446 if( !std::equal( std::begin( m_netClasses ), std::end( m_netClasses ),
447 std::begin( aOther.m_netClasses ), std::end( aOther.m_netClasses ),
448 netclassEntryEqual ) )
449 return false;
450
451 // m_defaultNetClass is held by shared_ptr and (per board.cpp / kicad_sexpr_parser.cpp
452 // setup paths) is mutated in place via GetDefaultNetclass()->SetClearance( ... ).
453 // shared_ptr identity is preserved across those edits, so a pointer-only check
454 // would silently drop the most common project edit. Compare contents.
455 if( static_cast<bool>( m_defaultNetClass ) != static_cast<bool>( aOther.m_defaultNetClass ) )
456 return false;
457
459 && !m_defaultNetClass->EqualsByPersistedFields( *aOther.m_defaultNetClass ) )
460 {
461 return false;
462 }
463
464 // m_netClassPatternAssignments stores std::unique_ptr<EDA_COMBINED_MATCHER>, so a naive
465 // std::equal would compare matcher pointer identity and report two settings instances built
466 // from identical input as unequal. Compare pattern text plus the assigned netclass name.
467 auto patternEqual = []( const auto& aLhs, const auto& aRhs )
468 {
469 if( !aLhs.first || !aRhs.first )
470 return aLhs.first.get() == aRhs.first.get() && aLhs.second == aRhs.second;
471
472 return aLhs.first->GetPattern() == aRhs.first->GetPattern() && aLhs.second == aRhs.second;
473 };
474
475 if( !std::equal( std::begin( m_netClassPatternAssignments ),
477 std::begin( aOther.m_netClassPatternAssignments ),
478 std::end( aOther.m_netClassPatternAssignments ),
479 patternEqual ) )
480 return false;
481
482 // m_netClassChainPatternAssignments is derived state, rebuilt from m_netChainNetClasses and
483 // the current chain membership. Equality is defined by persisted inputs only; including the
484 // derived list here would mark the project dirty whenever a rebuild produced a transient
485 // ordering difference.
486
487 if( !std::equal( std::begin( m_netClassLabelAssignments ),
488 std::end( m_netClassLabelAssignments ),
489 std::begin( aOther.m_netClassLabelAssignments ),
490 std::end( aOther.m_netClassLabelAssignments ) ) )
491 return false;
492
493 if( !std::equal( std::begin( m_netColorAssignments ), std::end( m_netColorAssignments ),
494 std::begin( aOther.m_netColorAssignments ),
495 std::end( aOther.m_netColorAssignments ) ) )
496 return false;
497
499 return false;
500
502 return false;
503
504 return true;
505}
506
507
509{
510 if( &aOther == this )
511 return;
512
513 // Flush the source's live field state into its JSON cache so the cache
514 // mirrors what we want to copy.
515 aOther.Store();
516
517 // CloneFrom() copies the JSON tree but leaves m_parent / m_path untouched,
518 // which is what we need: this instance must stay attached to its host
519 // project file so SaveToFile() walks the right m_nested_settings entry.
520 Internals()->CloneFrom( *aOther.Internals() );
521
522 // Repopulate our in-memory NETCLASS / pattern / color state from the cloned
523 // JSON. Load() rebuilds m_defaultNetClass, m_netClasses, fresh
524 // EDA_COMBINED_MATCHER instances for m_netClassPatternAssignments, and the
525 // remaining maps, decoupling our instance from aOther's lifetime.
526 Load();
527
528 // Load() repopulates persisted state but doesn't clear the derived caches.
529 // Drop them so stale lookups from this instance's pre-CopyFrom life
530 // (composite resolutions, implicit netclasses, chain-derived patterns
531 // rebuilt on every netlist update, and the per-net effective-class cache)
532 // don't survive the swap.
533 m_compositeNetClasses.clear();
534 m_impicitNetClasses.clear();
537}
538
539
541{
542 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
543 {
544 for( auto& netClass : m_internals->At( "classes" ).items() )
545 {
546 if( netClass.value().contains( "nets" ) && netClass.value()["nets"].is_array() )
547 {
548 nlohmann::json migrated = nlohmann::json::array();
549
550 for( auto& net : netClass.value()["nets"].items() )
551 migrated.push_back( ConvertToNewOverbarNotation( net.value().get<wxString>() ) );
552
553 netClass.value()["nets"] = migrated;
554 }
555 }
556 }
557
558 return true;
559}
560
561
563{
564 return true;
565}
566
567
569{
570 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
571 {
572 nlohmann::json patterns = nlohmann::json::array();
573
574 for( auto& netClass : m_internals->At( "classes" ).items() )
575 {
576 if( netClass.value().contains( "name" )
577 && netClass.value().contains( "nets" )
578 && netClass.value()["nets"].is_array() )
579 {
580 wxString netClassName = netClass.value()["name"].get<wxString>();
581
582 for( auto& net : netClass.value()["nets"].items() )
583 {
584 nlohmann::json pattern_json = {
585 { "pattern", net.value().get<wxString>() },
586 { "netclass", netClassName }
587 };
588
589 patterns.push_back( pattern_json );
590 }
591 }
592 }
593
594 m_internals->SetFromString( "netclass_patterns", patterns );
595 }
596
597 return true;
598}
599
600
602{
603 // Add priority field to netclasses
604 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
605 {
606 int priority = 0;
607
608 for( auto& netClass : m_internals->At( "classes" ).items() )
609 {
610 if( netClass.value()["name"].get<wxString>() == NETCLASS::Default )
611 netClass.value()["priority"] = std::numeric_limits<int>::max();
612 else
613 netClass.value()["priority"] = priority++;
614 }
615 }
616
617 // Move netclass assignments to a list
618 if( m_internals->contains( "netclass_assignments" )
619 && m_internals->At( "netclass_assignments" ).is_object() )
620 {
621 nlohmann::json migrated = {};
622
623 for( const auto& pair : m_internals->At( "netclass_assignments" ).items() )
624 {
625 nlohmann::json netclassesJson = nlohmann::json::array();
626
627 if( pair.value().get<wxString>() != wxEmptyString )
628 netclassesJson.push_back( pair.value() );
629
630 migrated[pair.key()] = netclassesJson;
631 }
632
633 m_internals->SetFromString( "netclass_assignments", migrated );
634 }
635
636 return true;
637}
638
639
641{
642 // Add tuning profile name field to netclasses
643 if( m_internals->contains( "classes" ) && m_internals->At( "classes" ).is_array() )
644 {
645 const wxString emptyStr = "";
646
647 for( auto& netClass : m_internals->At( "classes" ).items() )
648 netClass.value()["tuning_profile"] = emptyStr.ToUTF8();
649 }
650
651 return true;
652}
653
654
655void NET_SETTINGS::SetDefaultNetclass( std::shared_ptr<NETCLASS> netclass )
656{
657 m_defaultNetClass = netclass;
658}
659
660
661std::shared_ptr<NETCLASS> NET_SETTINGS::GetDefaultNetclass() const
662{
663 return m_defaultNetClass;
664}
665
666
667bool NET_SETTINGS::HasNetclass( const wxString& netclassName ) const
668{
669 return m_netClasses.find( netclassName ) != m_netClasses.end();
670}
671
672
673void NET_SETTINGS::SetNetclass( const wxString& netclassName, std::shared_ptr<NETCLASS>& netclass )
674{
675 m_netClasses[netclassName] = netclass;
676}
677
678
679void NET_SETTINGS::SetNetclasses( const std::map<wxString, std::shared_ptr<NETCLASS>>& netclasses )
680{
681 m_netClasses = netclasses;
683}
684
685
686const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetNetclasses() const
687{
688 return m_netClasses;
689}
690
691
692const std::map<wxString, std::shared_ptr<NETCLASS>>& NET_SETTINGS::GetCompositeNetclasses() const
693{
695}
696
697
699{
700 m_netClasses.clear();
701 m_impicitNetClasses.clear();
703}
704
705
706const std::map<wxString, std::set<wxString>>& NET_SETTINGS::GetNetclassLabelAssignments() const
707{
709}
710
711
716
717
718void NET_SETTINGS::ClearNetclassLabelAssignment( const wxString& netName )
719{
720 m_netClassLabelAssignments.erase( netName );
721}
722
723
724void NET_SETTINGS::SetNetclassLabelAssignment( const wxString& netName,
725 const std::set<wxString>& netclasses )
726{
727 m_netClassLabelAssignments[netName] = netclasses;
728}
729
730
732 const std::set<wxString>& netclasses )
733{
734 m_netClassLabelAssignments[netName].insert( netclasses.begin(), netclasses.end() );
735}
736
737
738bool NET_SETTINGS::HasNetclassLabelAssignment( const wxString& netName ) const
739{
740 return m_netClassLabelAssignments.find( netName ) != m_netClassLabelAssignments.end();
741}
742
743
744void NET_SETTINGS::SetNetclassPatternAssignment( const wxString& pattern, const wxString& netclass )
745{
746 // Expand bus patterns (vector buses and bus groups) to individual member patterns.
747 // This is necessary because the regex/wildcard matchers interpret brackets and braces
748 // as special characters, not as bus notation.
749 ForEachBusMember( pattern,
750 [&]( const wxString& memberPattern )
751 {
752 addSinglePatternAssignment( memberPattern, netclass );
753 } );
754
756}
757
758
759void NET_SETTINGS::addSinglePatternAssignment( const wxString& pattern, const wxString& netclass )
760{
761 // Avoid exact duplicates - these shouldn't cause problems, due to later de-duplication
762 // but they are unnecessary.
763 for( auto& assignment : m_netClassPatternAssignments )
764 {
765 if( assignment.first->GetPattern() == pattern && assignment.second == netclass )
766 return;
767 }
768
769 // No assignment, add a new one
771 { std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ), netclass } );
772}
773
774
776 std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&& netclassPatterns )
777{
778 m_netClassPatternAssignments = std::move( netclassPatterns );
780}
781
782
783std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&
788
789
794
795
796void NET_SETTINGS::SetChainPatternAssignment( NET_CHAIN_SOURCE aSource, const wxString& pattern,
797 const wxString& netclass )
798{
799 ForEachBusMember( pattern,
800 [&]( const wxString& memberPattern )
801 {
802 addSingleChainPatternAssignment( aSource, memberPattern, netclass );
803 } );
804
806}
807
808
810 const wxString& pattern,
811 const wxString& netclass )
812{
813 std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>& assignments =
815
816 for( auto& assignment : assignments )
817 {
818 if( !assignment.first )
819 continue;
820
821 if( assignment.first->GetPattern() == pattern && assignment.second == netclass )
822 return;
823 }
824
825 assignments.push_back(
826 { std::make_unique<EDA_COMBINED_MATCHER>( pattern, CTX_NETCLASS ), netclass } );
827}
828
829
835
836
837void NET_SETTINGS::ClearCacheForNet( const wxString& netName )
838{
839 if( m_effectiveNetclassCache.count( netName ) )
840 {
841 wxString compositeNetclassName = m_effectiveNetclassCache[netName]->GetName();
842 m_compositeNetClasses.erase( compositeNetclassName );
843 m_effectiveNetclassCache.erase( netName );
844 }
845}
846
847
853
854
855void NET_SETTINGS::SetNetColorAssignment( const wxString& netName, const KIGFX::COLOR4D& color )
856{
857 m_netColorAssignments[netName] = color;
858}
859
860
861const std::map<wxString, KIGFX::COLOR4D>& NET_SETTINGS::GetNetColorAssignments() const
862{
864}
865
866
871
872
873bool NET_SETTINGS::RenameNetPathPrefix( const wxString& aOldPrefix, const wxString& aNewPrefix )
874{
875 if( aOldPrefix.IsEmpty() || aOldPrefix == aNewPrefix )
876 return false;
877
878 bool changed = false;
879
880 // Patterns hold the path as plain text, so swap the leading prefix and rebuild the matcher.
881 for( auto& [matcher, netclass] : m_netClassPatternAssignments )
882 {
883 const wxString pattern = matcher->GetPattern();
884
885 if( pattern.StartsWith( aOldPrefix ) )
886 {
887 wxString updated = aNewPrefix + pattern.Mid( aOldPrefix.length() );
888 matcher = std::make_unique<EDA_COMBINED_MATCHER>( updated, CTX_NETCLASS );
889 changed = true;
890 }
891 }
892
893 // Net color keys are full net names, which carry the path too.
894 std::map<wxString, KIGFX::COLOR4D> updatedColors;
895
896 for( const auto& [netName, color] : m_netColorAssignments )
897 {
898 if( netName.StartsWith( aOldPrefix ) )
899 {
900 updatedColors[aNewPrefix + netName.Mid( aOldPrefix.length() )] = color;
901 changed = true;
902 }
903 else
904 {
905 updatedColors[netName] = color;
906 }
907 }
908
909 if( changed )
910 {
911 m_netColorAssignments = std::move( updatedColors );
913 }
914
915 return changed;
916}
917
918
919bool NET_SETTINGS::HasEffectiveNetClass( const wxString& aNetName ) const
920{
921 return m_effectiveNetclassCache.count( aNetName ) > 0;
922}
923
924
925std::shared_ptr<NETCLASS> NET_SETTINGS::GetCachedEffectiveNetClass( const wxString& aNetName ) const
926{
927 return m_effectiveNetclassCache.at( aNetName );
928}
929
930
931std::shared_ptr<NETCLASS> NET_SETTINGS::GetEffectiveNetClass( const wxString& aNetName )
932{
933 // Lambda to fetch an explicit netclass. Returns a nullptr if not found
934 auto getExplicitNetclass =
935 [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
936 {
937 if( netclass == NETCLASS::Default )
938 return m_defaultNetClass;
939
940 auto ii = m_netClasses.find( netclass );
941
942 if( ii == m_netClasses.end() )
943 return {};
944 else
945 return ii->second;
946 };
947
948 // Lambda to fetch or create an implicit netclass (defined with a label, but not configured)
949 // These are needed as while they do not provide any netclass parameters, they do now appear in
950 // DRC matching strings as an assigned netclass.
951 auto getOrAddImplicitNetcless =
952 [this]( const wxString& netclass ) -> std::shared_ptr<NETCLASS>
953 {
954 auto ii = m_impicitNetClasses.find( netclass );
955
956 if( ii == m_impicitNetClasses.end() )
957 {
958 std::shared_ptr<NETCLASS> nc = std::make_shared<NETCLASS>( netclass, false );
959 nc->SetPriority( std::numeric_limits<int>::max() - 1 ); // Priority > default netclass
960 m_impicitNetClasses[netclass] = nc;
961 return nc;
962 }
963 else
964 {
965 return ii->second;
966 }
967 };
968
969 // <no net> is forced to be part of the default netclass.
970 if( aNetName.IsEmpty() )
971 return m_defaultNetClass;
972
973 // First check if we have a cached resolved netclass
974 auto cacheItr = m_effectiveNetclassCache.find( aNetName );
975
976 if( cacheItr != m_effectiveNetclassCache.end() )
977 return cacheItr->second;
978
979 // No cache found - build a vector of all netclasses assigned to or matching this net
980 std::unordered_set<std::shared_ptr<NETCLASS>> resolvedNetclasses;
981
982 // First find explicit netclass assignments
983 auto it = m_netClassLabelAssignments.find( aNetName );
984
985 if( it != m_netClassLabelAssignments.end() && it->second.size() > 0 )
986 {
987 for( const wxString& netclassName : it->second )
988 {
989 std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
990
991 if( netclass )
992 {
993 resolvedNetclasses.insert( std::move( netclass ) );
994 }
995 else
996 {
997 resolvedNetclasses.insert( getOrAddImplicitNetcless( netclassName ) );
998 }
999 }
1000 }
1001
1002 // Now find any pattern-matched netclass assignments (user + chain-derived)
1003 auto applyPatternList =
1004 [&]( const std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>>&
1005 patterns )
1006 {
1007 for( const auto& [matcher, netclassName] : patterns )
1008 {
1009 if( matcher->StartsWith( aNetName ) )
1010 {
1011 std::shared_ptr<NETCLASS> netclass = getExplicitNetclass( netclassName );
1012
1013 if( netclass )
1014 resolvedNetclasses.insert( std::move( netclass ) );
1015 else
1016 resolvedNetclasses.insert( getOrAddImplicitNetcless( netclassName ) );
1017 }
1018 }
1019 };
1020
1021 applyPatternList( m_netClassPatternAssignments );
1022
1023 for( const auto& [source, chainPatterns] : m_netClassChainPatternAssignments )
1024 applyPatternList( chainPatterns );
1025
1026 // Handle zero resolved netclasses
1027 if( resolvedNetclasses.size() == 0 )
1028 {
1029 // For bus patterns, check if all members share the same netclass.
1030 // If they do, the bus inherits that netclass for coloring purposes.
1031 std::shared_ptr<NETCLASS> sharedNetclass;
1032 bool allSameNetclass = true;
1033 bool isBusPattern = false;
1034
1035 ForEachBusMember( aNetName,
1036 [&]( const wxString& member )
1037 {
1038 // If ForEachBusMember gives us back the same name, it's not a bus.
1039 // Skip to avoid infinite recursion.
1040 if( member == aNetName )
1041 return;
1042
1043 isBusPattern = true;
1044
1045 if( !allSameNetclass )
1046 return;
1047
1048 std::shared_ptr<NETCLASS> memberNc = GetEffectiveNetClass( member );
1049
1050 if( !sharedNetclass )
1051 {
1052 sharedNetclass = memberNc;
1053 }
1054 else if( memberNc->GetName() != sharedNetclass->GetName() )
1055 {
1056 allSameNetclass = false;
1057 }
1058 } );
1059
1060 if( isBusPattern && allSameNetclass && sharedNetclass
1061 && sharedNetclass->GetName() != NETCLASS::Default )
1062 {
1063 m_effectiveNetclassCache[aNetName] = sharedNetclass;
1064 return sharedNetclass;
1065 }
1066
1068
1069 return m_defaultNetClass;
1070 }
1071
1072 // Make and cache the effective netclass. Note that makeEffectiveNetclass will add the default
1073 // netclass to resolvedNetclasses if it is needed to complete the netclass paramters set. It
1074 // will also sort resolvedNetclasses by priority order.
1075 std::vector<NETCLASS*> netclassPtrs;
1076
1077 for( const std::shared_ptr<NETCLASS>& nc : resolvedNetclasses )
1078 netclassPtrs.push_back( nc.get() );
1079
1080 wxString name;
1081 name.Printf( "Effective for net: %s", aNetName );
1082 std::shared_ptr<NETCLASS> effectiveNetclass = std::make_shared<NETCLASS>( name, false );
1083 makeEffectiveNetclass( effectiveNetclass, netclassPtrs );
1084
1085 if( netclassPtrs.size() == 1 )
1086 {
1087 // No defaults were added - just return the primary netclass
1088 m_effectiveNetclassCache[aNetName] = *resolvedNetclasses.begin();
1089 return *resolvedNetclasses.begin();
1090 }
1091 else
1092 {
1093 effectiveNetclass->SetConstituentNetclasses( std::move( netclassPtrs ) );
1094
1095 m_compositeNetClasses[effectiveNetclass->GetName()] = effectiveNetclass;
1096 m_effectiveNetclassCache[aNetName] = effectiveNetclass;
1097
1098 return effectiveNetclass;
1099 }
1100}
1101
1102
1104{
1105 for( auto& [ncName, nc] : m_compositeNetClasses )
1106 {
1107 // Note this needs to be a copy in case we now need to add the default netclass
1108 std::vector<NETCLASS*> constituents = nc->GetConstituentNetclasses();
1109
1110 wxASSERT( constituents.size() > 0 );
1111
1112 // If the last netclass is Default, remove it (it will be re-added if still needed)
1113 if( ( *constituents.rbegin() )->GetName() == NETCLASS::Default )
1114 {
1115 constituents.pop_back();
1116 }
1117
1118 // Remake the netclass from original constituents
1119 nc->ResetParameters();
1120 makeEffectiveNetclass( nc, constituents );
1121 nc->SetConstituentNetclasses( std::move( constituents ) );
1122 }
1123}
1124
1125
1126void NET_SETTINGS::makeEffectiveNetclass( std::shared_ptr<NETCLASS>& effectiveNetclass,
1127 std::vector<NETCLASS*>& constituentNetclasses ) const
1128{
1129 // Sort the resolved netclasses by priority (highest first), with same-priority netclasses
1130 // ordered alphabetically
1131 std::sort( constituentNetclasses.begin(), constituentNetclasses.end(),
1132 []( NETCLASS* nc1, NETCLASS* nc2 )
1133 {
1134 int p1 = nc1->GetPriority();
1135 int p2 = nc2->GetPriority();
1136
1137 if( p1 < p2 )
1138 return true;
1139
1140 if (p1 == p2)
1141 return nc1->GetName().Cmp( nc2->GetName() ) < 0;
1142
1143 return false;
1144 } );
1145
1146 // Iterate from lowest priority netclass and fill effective netclass parameters
1147 for( auto itr = constituentNetclasses.rbegin(); itr != constituentNetclasses.rend(); ++itr )
1148 {
1149 NETCLASS* nc = *itr;
1150
1151 if( nc->HasClearance() )
1152 {
1153 effectiveNetclass->SetClearance( nc->GetClearance() );
1154 effectiveNetclass->SetClearanceParent( nc );
1155 }
1156
1157 if( nc->HasTrackWidth() )
1158 {
1159 effectiveNetclass->SetTrackWidth( nc->GetTrackWidth() );
1160 effectiveNetclass->SetTrackWidthParent( nc );
1161 }
1162
1163 if( nc->HasViaDiameter() )
1164 {
1165 effectiveNetclass->SetViaDiameter( nc->GetViaDiameter() );
1166 effectiveNetclass->SetViaDiameterParent( nc );
1167 }
1168
1169 if( nc->HasViaDrill() )
1170 {
1171 effectiveNetclass->SetViaDrill( nc->GetViaDrill() );
1172 effectiveNetclass->SetViaDrillParent( nc );
1173 }
1174
1175 if( nc->HasuViaDiameter() )
1176 {
1177 effectiveNetclass->SetuViaDiameter( nc->GetuViaDiameter() );
1178 effectiveNetclass->SetuViaDiameterParent( nc );
1179 }
1180
1181 if( nc->HasuViaDrill() )
1182 {
1183 effectiveNetclass->SetuViaDrill( nc->GetuViaDrill() );
1184 effectiveNetclass->SetuViaDrillParent( nc );
1185 }
1186
1187 if( nc->HasDiffPairWidth() )
1188 {
1189 effectiveNetclass->SetDiffPairWidth( nc->GetDiffPairWidth() );
1190 effectiveNetclass->SetDiffPairWidthParent( nc );
1191 }
1192
1193 if( nc->HasDiffPairGap() )
1194 {
1195 effectiveNetclass->SetDiffPairGap( nc->GetDiffPairGap() );
1196 effectiveNetclass->SetDiffPairGapParent( nc );
1197 }
1198
1199 if( nc->HasDiffPairViaGap() )
1200 {
1201 effectiveNetclass->SetDiffPairViaGap( nc->GetDiffPairViaGap() );
1202 effectiveNetclass->SetDiffPairViaGapParent( nc );
1203 }
1204
1205 if( nc->HasWireWidth() )
1206 {
1207 effectiveNetclass->SetWireWidth( nc->GetWireWidth() );
1208 effectiveNetclass->SetWireWidthParent( nc );
1209 }
1210
1211 if( nc->HasBusWidth() )
1212 {
1213 effectiveNetclass->SetBusWidth( nc->GetBusWidth() );
1214 effectiveNetclass->SetBusWidthParent( nc );
1215 }
1216
1217 if( nc->HasLineStyle() )
1218 {
1219 effectiveNetclass->SetLineStyle( nc->GetLineStyle() );
1220 effectiveNetclass->SetLineStyleParent( nc );
1221 }
1222
1223 COLOR4D pcbColor = nc->GetPcbColor();
1224
1225 if( pcbColor != COLOR4D::UNSPECIFIED )
1226 {
1227 effectiveNetclass->SetPcbColor( pcbColor );
1228 effectiveNetclass->SetPcbColorParent( nc );
1229 }
1230
1231 COLOR4D schColor = nc->GetSchematicColor();
1232
1233 if( schColor != COLOR4D::UNSPECIFIED )
1234 {
1235 effectiveNetclass->SetSchematicColor( schColor );
1236 effectiveNetclass->SetSchematicColorParent( nc );
1237 }
1238
1239 if( nc->HasTuningProfile() )
1240 {
1241 effectiveNetclass->SetTuningProfile( nc->GetTuningProfile() );
1242 effectiveNetclass->SetTuningProfileParent( nc );
1243 }
1244 }
1245
1246 // Fill in any required defaults
1247 if( addMissingDefaults( effectiveNetclass.get() ) )
1248 constituentNetclasses.push_back( m_defaultNetClass.get() );
1249}
1250
1251
1253{
1254 bool addedDefault = false;
1255
1256 if( !nc->HasClearance() )
1257 {
1258 addedDefault = true;
1259 nc->SetClearance( m_defaultNetClass->GetClearance() );
1261 }
1262
1263 if( !nc->HasTrackWidth() )
1264 {
1265 addedDefault = true;
1266 nc->SetTrackWidth( m_defaultNetClass->GetTrackWidth() );
1268 }
1269
1270 if( !nc->HasViaDiameter() )
1271 {
1272 addedDefault = true;
1273 nc->SetViaDiameter( m_defaultNetClass->GetViaDiameter() );
1275 }
1276
1277 if( !nc->HasViaDrill() )
1278 {
1279 addedDefault = true;
1280 nc->SetViaDrill( m_defaultNetClass->GetViaDrill() );
1282 }
1283
1284 if( !nc->HasuViaDiameter() )
1285 {
1286 addedDefault = true;
1287 nc->SetuViaDiameter( m_defaultNetClass->GetuViaDiameter() );
1289 }
1290
1291 if( !nc->HasuViaDrill() )
1292 {
1293 addedDefault = true;
1294 nc->SetuViaDrill( m_defaultNetClass->GetuViaDrill() );
1296 }
1297
1298 if( !nc->HasDiffPairWidth() )
1299 {
1300 addedDefault = true;
1301 nc->SetDiffPairWidth( m_defaultNetClass->GetDiffPairWidth() );
1303 }
1304
1305 if( !nc->HasDiffPairGap() )
1306 {
1307 addedDefault = true;
1308 nc->SetDiffPairGap( m_defaultNetClass->GetDiffPairGap() );
1310 }
1311
1312 // Currently this is only on the default netclass, and not editable in the setup panel
1313 // if( !nc->HasDiffPairViaGap() )
1314 // {
1315 // addedDefault = true;
1316 // nc->SetDiffPairViaGap( m_defaultNetClass->GetDiffPairViaGap() );
1317 // nc->SetDiffPairViaGapParent( m_defaultNetClass.get() );
1318 // }
1319
1320 if( !nc->HasWireWidth() )
1321 {
1322 addedDefault = true;
1323 nc->SetWireWidth( m_defaultNetClass->GetWireWidth() );
1325 }
1326
1327 if( !nc->HasBusWidth() )
1328 {
1329 addedDefault = true;
1330 nc->SetBusWidth( m_defaultNetClass->GetBusWidth() );
1332 }
1333
1334 // The tuning profile can be empty - only fill if a default tuning profile is set
1335 if( !nc->HasTuningProfile() && m_defaultNetClass->HasTuningProfile() )
1336 {
1337 addedDefault = true;
1338 nc->SetTuningProfile( m_defaultNetClass->GetTuningProfile() );
1340 }
1341
1342 return addedDefault;
1343}
1344
1345
1346std::shared_ptr<NETCLASS> NET_SETTINGS::GetNetClassByName( const wxString& aNetClassName ) const
1347{
1348 auto ii = m_netClasses.find( aNetClassName );
1349
1350 if( ii == m_netClasses.end() )
1351 return m_defaultNetClass;
1352 else
1353 return ii->second;
1354}
1355
1356
1357static bool isSuperSubOverbar( wxChar c )
1358{
1359 return c == '_' || c == '^' || c == '~';
1360}
1361
1362
1370static bool isEscaped( const wxString& aStr, size_t aPos )
1371{
1372 if( aPos == 0 )
1373 return false;
1374
1375 // Count consecutive backslashes before this position
1376 int backslashCount = 0;
1377 size_t pos = aPos;
1378
1379 while( pos > 0 && aStr[pos - 1] == '\\' )
1380 {
1381 backslashCount++;
1382 pos--;
1383 }
1384
1385 // If odd number of backslashes, the character is escaped
1386 return ( backslashCount % 2 ) == 1;
1387}
1388
1389
1390bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
1391 std::vector<wxString>* aMemberList )
1392{
1393 auto isDigit =
1394 []( wxChar c )
1395 {
1396 static wxString digits( wxT( "0123456789" ) );
1397 return digits.Contains( c );
1398 };
1399
1400 size_t busLen = aBus.length();
1401 size_t i = 0;
1402 wxString prefix;
1403 wxString suffix;
1404 wxString tmp;
1405 long begin = 0;
1406 long end = 0;
1407 int braceNesting = 0;
1408 bool fmtWrapsName = false;
1409 bool inQuotes = false;
1410
1411 prefix.reserve( busLen );
1412
1413 // Parse prefix
1414 //
1415 // Formatting markers (^{}, _{}, ~{}) can appear either as part of the prefix name
1416 // (e.g. I^{2}C[0..7]) or wrapping the range specifier (e.g. D_{[1..2]}).
1417 // We preserve formatting in the prefix and only strip it when the range bracket
1418 // appears inside formatting braces, indicating the formatting wraps the range.
1419 //
1420 for( ; i < busLen; ++i )
1421 {
1422 // Handle quoted strings (allows spaces inside)
1423 if( aBus[i] == '"' && !isEscaped( aBus, i ) )
1424 {
1425 inQuotes = !inQuotes;
1426 continue;
1427 }
1428
1429 if( inQuotes )
1430 {
1431 // Inside quotes, add characters directly (including spaces)
1432 if( aBus[i] == '\\' && i + 1 < busLen )
1433 {
1434 // Handle escaped characters inside quotes
1435 prefix += aBus[++i];
1436 }
1437 else
1438 {
1439 prefix += aBus[i];
1440 }
1441
1442 continue;
1443 }
1444
1445 if( aBus[i] == '{' )
1446 {
1447 if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
1448 {
1449 braceNesting++;
1450 prefix += wxT( '{' );
1451 continue;
1452 }
1453 else
1454 return false;
1455 }
1456 else if( aBus[i] == '}' )
1457 {
1458 braceNesting--;
1459 prefix += wxT( '}' );
1460 continue;
1461 }
1462
1463 // Handle backslash-escaped spaces
1464 if( aBus[i] == '\\' && i + 1 < busLen && aBus[i + 1] == ' ' )
1465 {
1466 prefix += aBus[++i];
1467 continue;
1468 }
1469
1470 // Unescaped space or ] in bus vector prefix is not allowed
1471 if( aBus[i] == ' ' || aBus[i] == ']' )
1472 return false;
1473
1474 if( aBus[i] == '[' )
1475 {
1476 if( braceNesting > 0 )
1477 {
1478 size_t fmtStart = prefix.rfind( wxT( '{' ) );
1479
1480 if( fmtStart != wxString::npos && fmtStart > 0
1481 && isSuperSubOverbar( prefix[fmtStart - 1] ) )
1482 {
1483 if( fmtStart == prefix.length() - 1 )
1484 {
1485 // '{' immediately precedes '[' (e.g. D_{[1..2]}).
1486 // The formatting decorates the range indices, not the
1487 // name itself.
1488 prefix.erase( fmtStart - 1 );
1489 }
1490 else
1491 {
1492 // Name characters exist between '{' and '[' (e.g.
1493 // ~{BE[0..3]}). The formatting wraps the signal name,
1494 // not the range.
1495 fmtWrapsName = true;
1496 }
1497 }
1498 }
1499
1500 break;
1501 }
1502
1503 prefix += aBus[i];
1504 }
1505
1506 // Parse start number
1507 //
1508 i++; // '[' character
1509
1510 if( i >= busLen )
1511 return false;
1512
1513 for( ; i < busLen; ++i )
1514 {
1515 if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
1516 {
1517 tmp.ToLong( &begin );
1518 i += 2;
1519 break;
1520 }
1521
1522 if( !isDigit( aBus[i] ) )
1523 return false;
1524
1525 tmp += aBus[i];
1526 }
1527
1528 // Parse end number
1529 //
1530 tmp = wxEmptyString;
1531
1532 if( i >= busLen )
1533 return false;
1534
1535 for( ; i < busLen; ++i )
1536 {
1537 if( aBus[i] == ']' )
1538 {
1539 tmp.ToLong( &end );
1540 ++i;
1541 break;
1542 }
1543
1544 if( !isDigit( aBus[i] ) )
1545 return false;
1546
1547 tmp += aBus[i];
1548 }
1549
1550 // Parse suffix
1551 //
1552 for( ; i < busLen; ++i )
1553 {
1554 if( aBus[i] == '}' )
1555 {
1556 braceNesting--;
1557
1558 if( fmtWrapsName )
1559 suffix += aBus[i];
1560 }
1561 else if( aBus[i] == '+' || aBus[i] == '-' || aBus[i] == 'P' || aBus[i] == 'N' )
1562 {
1563 suffix += aBus[i];
1564 }
1565 else
1566 {
1567 return false;
1568 }
1569 }
1570
1571 if( braceNesting != 0 )
1572 return false;
1573
1574 if( begin == end )
1575 return false;
1576 else if( begin > end )
1577 std::swap( begin, end );
1578
1579 if( aName )
1580 *aName = prefix;
1581
1582 if( aMemberList )
1583 {
1584 for( long idx = begin; idx <= end; ++idx )
1585 {
1586 wxString str = prefix;
1587 str << idx;
1588 str << suffix;
1589
1590 aMemberList->emplace_back( str );
1591 }
1592 }
1593
1594 return true;
1595}
1596
1597
1598bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
1599 std::vector<wxString>* aMemberList )
1600{
1601 size_t groupLen = aGroup.length();
1602 size_t i = 0;
1603 wxString prefix;
1604 wxString tmp;
1605 int braceNesting = 0;
1606 bool inQuotes = false;
1607
1608 prefix.reserve( groupLen );
1609
1610 // Escape spaces in member names so recursive parsing by ForEachBusMember works correctly.
1611 // Both quoted strings and backslash-escaped spaces collapse to bare spaces during parsing,
1612 // so we must re-escape them for subsequent ParseBusVector/ParseBusGroup calls.
1613 auto escapeSpacesForBus =
1614 []( const wxString& aMember ) -> wxString
1615 {
1616 wxString escaped;
1617 escaped.reserve( aMember.length() * 2 );
1618
1619 for( wxUniChar c : aMember )
1620 {
1621 if( c == ' ' )
1622 escaped += wxT( "\\ " );
1623 else
1624 escaped += c;
1625 }
1626
1627 return escaped;
1628 };
1629
1630 // Parse prefix
1631 //
1632 // Formatting markers (^{}, _{}, ~{}) in the prefix are part of the group name
1633 // and must be preserved. The member-list opening brace is distinguished by NOT
1634 // being preceded by a formatting character.
1635 //
1636 for( ; i < groupLen; ++i )
1637 {
1638 // Handle quoted strings (allows spaces inside)
1639 if( aGroup[i] == '"' && !isEscaped( aGroup, i ) )
1640 {
1641 inQuotes = !inQuotes;
1642 continue;
1643 }
1644
1645 if( inQuotes )
1646 {
1647 // Inside quotes, add characters directly (including spaces)
1648 if( aGroup[i] == '\\' && i + 1 < groupLen )
1649 {
1650 // Handle escaped characters inside quotes
1651 prefix += aGroup[++i];
1652 }
1653 else
1654 {
1655 prefix += aGroup[i];
1656 }
1657
1658 continue;
1659 }
1660
1661 if( aGroup[i] == '{' )
1662 {
1663 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
1664 {
1665 braceNesting++;
1666 prefix += wxT( '{' );
1667 continue;
1668 }
1669 else
1670 break;
1671 }
1672 else if( aGroup[i] == '}' )
1673 {
1674 braceNesting--;
1675 prefix += wxT( '}' );
1676 continue;
1677 }
1678
1679 // Handle backslash-escaped spaces
1680 if( aGroup[i] == '\\' && i + 1 < groupLen && aGroup[i + 1] == ' ' )
1681 {
1682 prefix += aGroup[++i];
1683 continue;
1684 }
1685
1686 // Unescaped space, [, or ] in bus group prefix is not allowed
1687 if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
1688 return false;
1689
1690 prefix += aGroup[i];
1691 }
1692
1693 if( braceNesting != 0 )
1694 return false;
1695
1696 if( aName )
1697 *aName = prefix;
1698
1699 // Parse members
1700 //
1701 i++; // '{' character
1702
1703 if( i >= groupLen )
1704 return false;
1705
1706 inQuotes = false;
1707
1708 for( ; i < groupLen; ++i )
1709 {
1710 // Handle quoted strings (allows spaces inside member names)
1711 if( aGroup[i] == '"' && !isEscaped( aGroup, i ) )
1712 {
1713 inQuotes = !inQuotes;
1714 continue;
1715 }
1716
1717 if( inQuotes )
1718 {
1719 // Inside quotes, add characters directly (including spaces)
1720 if( aGroup[i] == '\\' && i + 1 < groupLen )
1721 {
1722 // Handle escaped characters inside quotes
1723 tmp += aGroup[++i];
1724 }
1725 else
1726 {
1727 tmp += aGroup[i];
1728 }
1729
1730 continue;
1731 }
1732
1733 if( aGroup[i] == '{' )
1734 {
1735 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
1736 {
1737 braceNesting++;
1738
1739 // Keep the full formatting notation (e.g. ~{CAS}) in the member name.
1740 // A net named ~{CAS} is distinct from CAS, and stripping the marker
1741 // would lose that identity. Vector bus members like D_{[1..2]} also
1742 // preserve their subscript so recursive ForEachBusMember can parse them.
1743 tmp += wxT( '{' );
1744 continue;
1745 }
1746 else
1747 return false;
1748 }
1749 else if( aGroup[i] == '}' )
1750 {
1751 if( braceNesting )
1752 {
1753 braceNesting--;
1754 tmp += wxT( '}' );
1755 continue;
1756 }
1757 else
1758 {
1759 if( aMemberList && !tmp.IsEmpty() )
1760 aMemberList->push_back( EscapeString( escapeSpacesForBus( tmp ), CTX_NETNAME ) );
1761
1762 return true;
1763 }
1764 }
1765
1766 // Handle backslash-escaped spaces in member names
1767 if( aGroup[i] == '\\' && i + 1 < groupLen && aGroup[i + 1] == ' ' )
1768 {
1769 tmp += aGroup[++i];
1770 continue;
1771 }
1772
1773 // Unescaped space or comma separates members
1774 if( aGroup[i] == ' ' || aGroup[i] == ',' )
1775 {
1776 if( aMemberList && !tmp.IsEmpty() )
1777 aMemberList->push_back( EscapeString( escapeSpacesForBus( tmp ), CTX_NETNAME ) );
1778
1779 tmp.Clear();
1780 continue;
1781 }
1782
1783 tmp += aGroup[i];
1784 }
1785
1786 return false;
1787}
1788
1789
1790void NET_SETTINGS::ForEachBusMember( const wxString& aBusPattern,
1791 const std::function<void( const wxString& )>& aFunction )
1792{
1793 std::vector<wxString> members;
1794
1795 if( ParseBusVector( aBusPattern, nullptr, &members ) )
1796 {
1797 // Vector bus: call function for each expanded member
1798 for( const wxString& member : members )
1799 aFunction( member );
1800 }
1801 else if( ParseBusGroup( aBusPattern, nullptr, &members ) )
1802 {
1803 // Bus group: recursively expand each member (which may itself be a vector or group)
1804 for( const wxString& member : members )
1805 ForEachBusMember( member, aFunction );
1806 }
1807 else
1808 {
1809 // Not a bus pattern: call function with the original pattern
1810 aFunction( aBusPattern );
1811 }
1812}
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...
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...
bool RenameNetPathPrefix(const wxString &aOldPrefix, const wxString &aNewPrefix)
Retarget netclass patterns and net colors after a path prefix changes (sheet rename).
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()
std::map< wxString, wxString > m_netChainNetClasses
Map of net-chain name -> netclass name applied to every net in the chain.
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.
std::map< NET_CHAIN_SOURCE, std::vector< std::pair< std::unique_ptr< EDA_COMBINED_MATCHER >, wxString > > > m_netClassChainPatternAssignments
Chain-derived netclass pattern assignments, keyed by the editor that derived them.
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.
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::shared_ptr< NETCLASS > GetDefaultNetclass() const
Gets the default netclass for the project.
void SetChainPatternAssignment(NET_CHAIN_SOURCE aSource, const wxString &pattern, const wxString &netclass)
Sets a chain-derived netclass pattern assignment owned by aSource.
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 ClearChainPatternAssignments(NET_CHAIN_SOURCE aSource)
Clears the chain-derived pattern assignments owned by aSource, leaving the other source's entries in ...
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.
void addSingleChainPatternAssignment(NET_CHAIN_SOURCE aSource, const wxString &pattern, const wxString &netclass)
Adds a single chain-derived pattern assignment without bus expansion (internal helper)
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:299
bool isDigit(char cc)
Definition dsnlexer.cpp:465
#define _(s)
@ CTX_NETCLASS
nlohmann::json json
Definition gerbview.cpp:50
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 >())
NET_CHAIN_SOURCE
Owner of a set of chain-derived netclass pattern assignments.
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