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