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<std::shared_ptr<NETCLASS>> netclassPtrs( resolvedNetclasses.begin(), resolvedNetclasses.end() );
1145
1146 wxString name;
1147 name.Printf( "Effective for net: %s", aNetName );
1148 std::shared_ptr<NETCLASS> effectiveNetclass = std::make_shared<NETCLASS>( name, false );
1149 makeEffectiveNetclass( effectiveNetclass, netclassPtrs );
1150
1151 if( netclassPtrs.size() == 1 )
1152 {
1153 // No defaults were added - just return the primary netclass
1154 m_effectiveNetclassCache[aNetName] = *resolvedNetclasses.begin();
1155 return *resolvedNetclasses.begin();
1156 }
1157 else
1158 {
1159 effectiveNetclass->SetConstituentOwners( std::move( netclassPtrs ) );
1160
1161 m_compositeNetClasses[effectiveNetclass->GetName()] = effectiveNetclass;
1162 m_effectiveNetclassCache[aNetName] = effectiveNetclass;
1163
1164 return effectiveNetclass;
1165 }
1166}
1167
1168
1170{
1171 for( auto& [ncName, nc] : m_compositeNetClasses )
1172 {
1173 // Note this needs to be a copy in case we now need to add the default netclass
1174 std::vector<std::shared_ptr<NETCLASS>> constituents = nc->GetConstituentOwners();
1175
1176 wxASSERT( constituents.size() > 0 );
1177
1178 // If the last netclass is Default, remove it (it will be re-added if still needed)
1179 if( ( *constituents.rbegin() )->GetName() == NETCLASS::Default )
1180 {
1181 constituents.pop_back();
1182 }
1183
1184 // Remake the netclass from original constituents
1185 nc->ResetParameters();
1186 makeEffectiveNetclass( nc, constituents );
1187 nc->SetConstituentOwners( std::move( constituents ) );
1188 }
1189}
1190
1191
1192void NET_SETTINGS::makeEffectiveNetclass( std::shared_ptr<NETCLASS>& effectiveNetclass,
1193 std::vector<std::shared_ptr<NETCLASS>>& constituentNetclasses ) const
1194{
1195 // Sort the resolved netclasses by priority (highest first), with same-priority netclasses
1196 // ordered alphabetically
1197 std::sort( constituentNetclasses.begin(), constituentNetclasses.end(),
1198 []( const std::shared_ptr<NETCLASS>& nc1, const std::shared_ptr<NETCLASS>& nc2 )
1199 {
1200 int p1 = nc1->GetPriority();
1201 int p2 = nc2->GetPriority();
1202
1203 if( p1 < p2 )
1204 return true;
1205
1206 if (p1 == p2)
1207 return nc1->GetName().Cmp( nc2->GetName() ) < 0;
1208
1209 return false;
1210 } );
1211
1212 // Iterate from lowest priority netclass and fill effective netclass parameters
1213 for( auto itr = constituentNetclasses.rbegin(); itr != constituentNetclasses.rend(); ++itr )
1214 {
1215 NETCLASS* nc = itr->get();
1216
1217 if( nc->HasClearance() )
1218 {
1219 effectiveNetclass->SetClearance( nc->GetClearance() );
1220 effectiveNetclass->SetClearanceParent( nc );
1221 }
1222
1223 if( nc->HasTrackWidth() )
1224 {
1225 effectiveNetclass->SetTrackWidth( nc->GetTrackWidth() );
1226 effectiveNetclass->SetTrackWidthParent( nc );
1227 }
1228
1229 if( nc->HasViaDiameter() )
1230 {
1231 effectiveNetclass->SetViaDiameter( nc->GetViaDiameter() );
1232 effectiveNetclass->SetViaDiameterParent( nc );
1233 }
1234
1235 if( nc->HasViaDrill() )
1236 {
1237 effectiveNetclass->SetViaDrill( nc->GetViaDrill() );
1238 effectiveNetclass->SetViaDrillParent( nc );
1239 }
1240
1241 if( nc->HasuViaDiameter() )
1242 {
1243 effectiveNetclass->SetuViaDiameter( nc->GetuViaDiameter() );
1244 effectiveNetclass->SetuViaDiameterParent( nc );
1245 }
1246
1247 if( nc->HasuViaDrill() )
1248 {
1249 effectiveNetclass->SetuViaDrill( nc->GetuViaDrill() );
1250 effectiveNetclass->SetuViaDrillParent( nc );
1251 }
1252
1253 if( nc->HasDiffPairWidth() )
1254 {
1255 effectiveNetclass->SetDiffPairWidth( nc->GetDiffPairWidth() );
1256 effectiveNetclass->SetDiffPairWidthParent( nc );
1257 }
1258
1259 if( nc->HasDiffPairGap() )
1260 {
1261 effectiveNetclass->SetDiffPairGap( nc->GetDiffPairGap() );
1262 effectiveNetclass->SetDiffPairGapParent( nc );
1263 }
1264
1265 if( nc->HasDiffPairViaGap() )
1266 {
1267 effectiveNetclass->SetDiffPairViaGap( nc->GetDiffPairViaGap() );
1268 effectiveNetclass->SetDiffPairViaGapParent( nc );
1269 }
1270
1271 if( nc->HasWireWidth() )
1272 {
1273 effectiveNetclass->SetWireWidth( nc->GetWireWidth() );
1274 effectiveNetclass->SetWireWidthParent( nc );
1275 }
1276
1277 if( nc->HasBusWidth() )
1278 {
1279 effectiveNetclass->SetBusWidth( nc->GetBusWidth() );
1280 effectiveNetclass->SetBusWidthParent( nc );
1281 }
1282
1283 if( nc->HasLineStyle() )
1284 {
1285 effectiveNetclass->SetLineStyle( nc->GetLineStyle() );
1286 effectiveNetclass->SetLineStyleParent( nc );
1287 }
1288
1289 COLOR4D pcbColor = nc->GetPcbColor();
1290
1291 if( pcbColor != COLOR4D::UNSPECIFIED )
1292 {
1293 effectiveNetclass->SetPcbColor( pcbColor );
1294 effectiveNetclass->SetPcbColorParent( nc );
1295 }
1296
1297 COLOR4D schColor = nc->GetSchematicColor();
1298
1299 if( schColor != COLOR4D::UNSPECIFIED )
1300 {
1301 effectiveNetclass->SetSchematicColor( schColor );
1302 effectiveNetclass->SetSchematicColorParent( nc );
1303 }
1304
1305 if( nc->HasTuningProfile() )
1306 {
1307 effectiveNetclass->SetTuningProfile( nc->GetTuningProfile() );
1308 effectiveNetclass->SetTuningProfileParent( nc );
1309 }
1310 }
1311
1312 // Fill in any required defaults
1313 if( addMissingDefaults( effectiveNetclass.get() ) )
1314 constituentNetclasses.push_back( m_defaultNetClass );
1315}
1316
1317
1319{
1320 bool addedDefault = false;
1321
1322 if( !nc->HasClearance() )
1323 {
1324 addedDefault = true;
1325 nc->SetClearance( m_defaultNetClass->GetClearance() );
1327 }
1328
1329 if( !nc->HasTrackWidth() )
1330 {
1331 addedDefault = true;
1332 nc->SetTrackWidth( m_defaultNetClass->GetTrackWidth() );
1334 }
1335
1336 if( !nc->HasViaDiameter() )
1337 {
1338 addedDefault = true;
1339 nc->SetViaDiameter( m_defaultNetClass->GetViaDiameter() );
1341 }
1342
1343 if( !nc->HasViaDrill() )
1344 {
1345 addedDefault = true;
1346 nc->SetViaDrill( m_defaultNetClass->GetViaDrill() );
1348 }
1349
1350 if( !nc->HasuViaDiameter() )
1351 {
1352 addedDefault = true;
1353 nc->SetuViaDiameter( m_defaultNetClass->GetuViaDiameter() );
1355 }
1356
1357 if( !nc->HasuViaDrill() )
1358 {
1359 addedDefault = true;
1360 nc->SetuViaDrill( m_defaultNetClass->GetuViaDrill() );
1362 }
1363
1364 if( !nc->HasDiffPairWidth() )
1365 {
1366 addedDefault = true;
1367 nc->SetDiffPairWidth( m_defaultNetClass->GetDiffPairWidth() );
1369 }
1370
1371 if( !nc->HasDiffPairGap() )
1372 {
1373 addedDefault = true;
1374 nc->SetDiffPairGap( m_defaultNetClass->GetDiffPairGap() );
1376 }
1377
1378 // Currently this is only on the default netclass, and not editable in the setup panel
1379 // if( !nc->HasDiffPairViaGap() )
1380 // {
1381 // addedDefault = true;
1382 // nc->SetDiffPairViaGap( m_defaultNetClass->GetDiffPairViaGap() );
1383 // nc->SetDiffPairViaGapParent( m_defaultNetClass.get() );
1384 // }
1385
1386 if( !nc->HasWireWidth() )
1387 {
1388 addedDefault = true;
1389 nc->SetWireWidth( m_defaultNetClass->GetWireWidth() );
1391 }
1392
1393 if( !nc->HasBusWidth() )
1394 {
1395 addedDefault = true;
1396 nc->SetBusWidth( m_defaultNetClass->GetBusWidth() );
1398 }
1399
1400 // The tuning profile can be empty - only fill if a default tuning profile is set
1401 if( !nc->HasTuningProfile() && m_defaultNetClass->HasTuningProfile() )
1402 {
1403 addedDefault = true;
1404 nc->SetTuningProfile( m_defaultNetClass->GetTuningProfile() );
1406 }
1407
1408 return addedDefault;
1409}
1410
1411
1412std::shared_ptr<NETCLASS> NET_SETTINGS::GetNetClassByName( const wxString& aNetClassName ) const
1413{
1414 auto ii = m_netClasses.find( aNetClassName );
1415
1416 if( ii == m_netClasses.end() )
1417 return m_defaultNetClass;
1418 else
1419 return ii->second;
1420}
1421
1422
1423static bool isSuperSubOverbar( wxChar c )
1424{
1425 return c == '_' || c == '^' || c == '~';
1426}
1427
1428
1436static bool isEscaped( const wxString& aStr, size_t aPos )
1437{
1438 if( aPos == 0 )
1439 return false;
1440
1441 // Count consecutive backslashes before this position
1442 int backslashCount = 0;
1443 size_t pos = aPos;
1444
1445 while( pos > 0 && aStr[pos - 1] == '\\' )
1446 {
1447 backslashCount++;
1448 pos--;
1449 }
1450
1451 // If odd number of backslashes, the character is escaped
1452 return ( backslashCount % 2 ) == 1;
1453}
1454
1455
1456bool NET_SETTINGS::ParseBusVector( const wxString& aBus, wxString* aName,
1457 std::vector<wxString>* aMemberList )
1458{
1459 auto isDigit =
1460 []( wxChar c )
1461 {
1462 static wxString digits( wxT( "0123456789" ) );
1463 return digits.Contains( c );
1464 };
1465
1466 size_t busLen = aBus.length();
1467 size_t i = 0;
1468 wxString prefix;
1469 wxString suffix;
1470 wxString tmp;
1471 long begin = 0;
1472 long end = 0;
1473 int braceNesting = 0;
1474 bool fmtWrapsName = false;
1475 bool inQuotes = false;
1476 bool parsedEnd = false;
1477 bool padded = false;
1478 size_t width = 0;
1479
1480 prefix.reserve( busLen );
1481
1482 // Parse prefix
1483 //
1484 // Formatting markers (^{}, _{}, ~{}) can appear either as part of the prefix name
1485 // (e.g. I^{2}C[0..7]) or wrapping the range specifier (e.g. D_{[1..2]}).
1486 // We preserve formatting in the prefix and only strip it when the range bracket
1487 // appears inside formatting braces, indicating the formatting wraps the range.
1488 //
1489 for( ; i < busLen; ++i )
1490 {
1491 // Handle quoted strings (allows spaces inside)
1492 if( aBus[i] == '"' && !isEscaped( aBus, i ) )
1493 {
1494 inQuotes = !inQuotes;
1495 continue;
1496 }
1497
1498 if( inQuotes )
1499 {
1500 // Inside quotes, add characters directly (including spaces)
1501 if( aBus[i] == '\\' && i + 1 < busLen )
1502 {
1503 // Handle escaped characters inside quotes
1504 prefix += aBus[++i];
1505 }
1506 else
1507 {
1508 prefix += aBus[i];
1509 }
1510
1511 continue;
1512 }
1513
1514 if( aBus[i] == '{' )
1515 {
1516 if( i > 0 && isSuperSubOverbar( aBus[i-1] ) )
1517 {
1518 braceNesting++;
1519 prefix += wxT( '{' );
1520 continue;
1521 }
1522 else
1523 return false;
1524 }
1525 else if( aBus[i] == '}' )
1526 {
1527 braceNesting--;
1528 prefix += wxT( '}' );
1529 continue;
1530 }
1531
1532 // Handle backslash-escaped spaces
1533 if( aBus[i] == '\\' && i + 1 < busLen && aBus[i + 1] == ' ' )
1534 {
1535 prefix += aBus[++i];
1536 continue;
1537 }
1538
1539 // Unescaped space or ] in bus vector prefix is not allowed
1540 if( aBus[i] == ' ' || aBus[i] == ']' )
1541 return false;
1542
1543 if( aBus[i] == '[' )
1544 {
1545 if( braceNesting > 0 )
1546 {
1547 size_t fmtStart = prefix.rfind( wxT( '{' ) );
1548
1549 if( fmtStart != wxString::npos && fmtStart > 0
1550 && isSuperSubOverbar( prefix[fmtStart - 1] ) )
1551 {
1552 if( fmtStart == prefix.length() - 1 )
1553 {
1554 // '{' immediately precedes '[' (e.g. D_{[1..2]}).
1555 // The formatting decorates the range indices, not the
1556 // name itself.
1557 prefix.erase( fmtStart - 1 );
1558 }
1559 else
1560 {
1561 // Name characters exist between '{' and '[' (e.g.
1562 // ~{BE[0..3]}). The formatting wraps the signal name,
1563 // not the range.
1564 fmtWrapsName = true;
1565 }
1566 }
1567 }
1568
1569 break;
1570 }
1571
1572 prefix += aBus[i];
1573 }
1574
1575 // Parse start number
1576 //
1577 i++; // '[' character
1578
1579 if( i >= busLen )
1580 return false;
1581
1582 for( ; i < busLen; ++i )
1583 {
1584 if( aBus[i] == '.' && i + 1 < busLen && aBus[i+1] == '.' )
1585 {
1586 if( tmp.IsEmpty() || !tmp.ToLong( &begin ) )
1587 return false;
1588
1589 width = tmp.length();
1590 padded = width > 1 && tmp[0] == '0';
1591 i += 2;
1592 break;
1593 }
1594
1595 if( !isDigit( aBus[i] ) )
1596 return false;
1597
1598 tmp += aBus[i];
1599 }
1600
1601 // Parse end number
1602 //
1603 tmp = wxEmptyString;
1604
1605 if( i >= busLen )
1606 return false;
1607
1608 for( ; i < busLen; ++i )
1609 {
1610 if( aBus[i] == ']' )
1611 {
1612 if( tmp.IsEmpty() || !tmp.ToLong( &end ) )
1613 return false;
1614
1615 padded |= tmp.length() > 1 && tmp[0] == '0';
1616 width = std::max( width, tmp.length() );
1617 parsedEnd = true;
1618 ++i;
1619 break;
1620 }
1621
1622 if( !isDigit( aBus[i] ) )
1623 return false;
1624
1625 tmp += aBus[i];
1626 }
1627
1628 if( !parsedEnd )
1629 return false;
1630
1631 // Parse suffix
1632 //
1633 for( ; i < busLen; ++i )
1634 {
1635 if( aBus[i] == '}' )
1636 {
1637 braceNesting--;
1638
1639 if( fmtWrapsName )
1640 suffix += aBus[i];
1641 }
1642 else if( aBus[i] == '+' || aBus[i] == '-' || aBus[i] == 'P' || aBus[i] == 'N' )
1643 {
1644 suffix += aBus[i];
1645 }
1646 else
1647 {
1648 return false;
1649 }
1650 }
1651
1652 if( braceNesting != 0 )
1653 return false;
1654
1655 if( begin == end )
1656 return false;
1657 else if( begin > end )
1658 std::swap( begin, end );
1659
1660 if( aName )
1661 *aName = prefix;
1662
1663 if( aMemberList )
1664 {
1665 // We can overflow the counter with the increment, so idx <= end is not safe here.
1666 for( long idx = begin;; ++idx )
1667 {
1668 wxString number;
1669 number << idx;
1670 wxString str = prefix;
1671
1672 if( padded && number.length() < width )
1673 str += wxString( '0', width - number.length() );
1674
1675 str << number << suffix;
1676 aMemberList->emplace_back( str );
1677
1678 if( idx == end )
1679 break;
1680 }
1681 }
1682
1683 return true;
1684}
1685
1686
1687bool NET_SETTINGS::ParseBusGroup( const wxString& aGroup, wxString* aName,
1688 std::vector<wxString>* aMemberList, size_t* aPrefixEnd )
1689{
1690 size_t groupLen = aGroup.length();
1691 size_t i = 0;
1692 wxString prefix;
1693 wxString tmp;
1694 int braceNesting = 0;
1695 bool inQuotes = false;
1696
1697 prefix.reserve( groupLen );
1698
1699 // Escape spaces in member names so recursive parsing by ForEachBusMember works correctly.
1700 // Both quoted strings and backslash-escaped spaces collapse to bare spaces during parsing,
1701 // so we must re-escape them for subsequent ParseBusVector/ParseBusGroup calls.
1702 auto escapeSpacesForBus =
1703 []( const wxString& aMember ) -> wxString
1704 {
1705 wxString escaped;
1706 escaped.reserve( aMember.length() * 2 );
1707
1708 for( wxUniChar c : aMember )
1709 {
1710 if( c == ' ' )
1711 escaped += wxT( "\\ " );
1712 else
1713 escaped += c;
1714 }
1715
1716 return escaped;
1717 };
1718
1719 // Parse prefix
1720 //
1721 // Formatting markers (^{}, _{}, ~{}) in the prefix are part of the group name
1722 // and must be preserved. The member-list opening brace is distinguished by NOT
1723 // being preceded by a formatting character.
1724 //
1725 for( ; i < groupLen; ++i )
1726 {
1727 // Handle quoted strings (allows spaces inside)
1728 if( aGroup[i] == '"' && !isEscaped( aGroup, i ) )
1729 {
1730 inQuotes = !inQuotes;
1731 continue;
1732 }
1733
1734 if( inQuotes )
1735 {
1736 // Inside quotes, add characters directly (including spaces)
1737 if( aGroup[i] == '\\' && i + 1 < groupLen )
1738 {
1739 // Handle escaped characters inside quotes
1740 prefix += aGroup[++i];
1741 }
1742 else
1743 {
1744 prefix += aGroup[i];
1745 }
1746
1747 continue;
1748 }
1749
1750 if( aGroup[i] == '{' )
1751 {
1752 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
1753 {
1754 braceNesting++;
1755 prefix += wxT( '{' );
1756 continue;
1757 }
1758 else
1759 break;
1760 }
1761 else if( aGroup[i] == '}' )
1762 {
1763 braceNesting--;
1764 prefix += wxT( '}' );
1765 continue;
1766 }
1767
1768 // Handle backslash-escaped spaces
1769 if( aGroup[i] == '\\' && i + 1 < groupLen && aGroup[i + 1] == ' ' )
1770 {
1771 prefix += aGroup[++i];
1772 continue;
1773 }
1774
1775 // Unescaped space, [, or ] in bus group prefix is not allowed
1776 if( aGroup[i] == ' ' || aGroup[i] == '[' || aGroup[i] == ']' )
1777 return false;
1778
1779 prefix += aGroup[i];
1780 }
1781
1782 if( braceNesting != 0 )
1783 return false;
1784
1785 if( aName )
1786 *aName = prefix;
1787
1788 const size_t prefixEnd = i;
1789
1790 // Parse members
1791 //
1792 i++; // '{' character
1793
1794 if( i >= groupLen )
1795 return false;
1796
1797 inQuotes = false;
1798
1799 for( ; i < groupLen; ++i )
1800 {
1801 // Handle quoted strings (allows spaces inside member names)
1802 if( aGroup[i] == '"' && !isEscaped( aGroup, i ) )
1803 {
1804 inQuotes = !inQuotes;
1805 continue;
1806 }
1807
1808 if( inQuotes )
1809 {
1810 // Inside quotes, add characters directly (including spaces)
1811 if( aGroup[i] == '\\' && i + 1 < groupLen )
1812 {
1813 // Handle escaped characters inside quotes
1814 tmp += aGroup[++i];
1815 }
1816 else
1817 {
1818 tmp += aGroup[i];
1819 }
1820
1821 continue;
1822 }
1823
1824 if( aGroup[i] == '{' )
1825 {
1826 if( i > 0 && isSuperSubOverbar( aGroup[i-1] ) )
1827 {
1828 braceNesting++;
1829
1830 // Keep the full formatting notation (e.g. ~{CAS}) in the member name.
1831 // A net named ~{CAS} is distinct from CAS, and stripping the marker
1832 // would lose that identity. Vector bus members like D_{[1..2]} also
1833 // preserve their subscript so recursive ForEachBusMember can parse them.
1834 tmp += wxT( '{' );
1835 continue;
1836 }
1837 else
1838 return false;
1839 }
1840 else if( aGroup[i] == '}' )
1841 {
1842 if( braceNesting )
1843 {
1844 braceNesting--;
1845 tmp += wxT( '}' );
1846 continue;
1847 }
1848 else
1849 {
1850 if( aMemberList && !tmp.IsEmpty() )
1851 aMemberList->push_back( EscapeString( escapeSpacesForBus( tmp ), CTX_NETNAME ) );
1852
1853 if( aPrefixEnd )
1854 *aPrefixEnd = prefixEnd;
1855
1856 return true;
1857 }
1858 }
1859
1860 // Handle backslash-escaped spaces in member names
1861 if( aGroup[i] == '\\' && i + 1 < groupLen && aGroup[i + 1] == ' ' )
1862 {
1863 tmp += aGroup[++i];
1864 continue;
1865 }
1866
1867 // Unescaped space or comma separates members
1868 if( aGroup[i] == ' ' || aGroup[i] == ',' )
1869 {
1870 if( aMemberList && !tmp.IsEmpty() )
1871 aMemberList->push_back( EscapeString( escapeSpacesForBus( tmp ), CTX_NETNAME ) );
1872
1873 tmp.Clear();
1874 continue;
1875 }
1876
1877 tmp += aGroup[i];
1878 }
1879
1880 return false;
1881}
1882
1883
1884void NET_SETTINGS::ForEachBusMember( const wxString& aBusPattern,
1885 const std::function<void( const wxString& )>& aFunction )
1886{
1887 std::vector<wxString> members;
1888
1889 if( ParseBusVector( aBusPattern, nullptr, &members ) )
1890 {
1891 // Vector bus: call function for each expanded member
1892 for( const wxString& member : members )
1893 aFunction( member );
1894 }
1895 else if( ParseBusGroup( aBusPattern, nullptr, &members ) )
1896 {
1897 // Bus group: recursively expand each member (which may itself be a vector or group)
1898 for( const wxString& member : members )
1899 ForEachBusMember( member, aFunction );
1900 }
1901 else
1902 {
1903 // Not a bus pattern: call function with the original pattern
1904 aFunction( aBusPattern );
1905 }
1906}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
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:44
void SetViaDiameter(int aDia)
Definition netclass.h:160
void SetViaDrill(int aSize)
Definition netclass.h:168
bool HasLineStyle() const
Definition netclass.h:258
int GetViaDiameter() const
Definition netclass.h:158
int GetViaDrill() const
Definition netclass.h:166
void SetWireWidthParent(NETCLASS *parent)
Definition netclass.h:233
static const char Default[]
the name of the default NETCLASS
Definition netclass.h:46
void SetuViaDrillParent(NETCLASS *parent)
Definition netclass.h:186
bool HasBusWidth() const
Definition netclass.h:236
bool HasuViaDrill() const
Definition netclass.h:181
void SetDiffPairWidthParent(NETCLASS *parent)
Definition netclass.h:194
void SetuViaDiameter(int aSize)
Definition netclass.h:176
void SetDiffPairWidth(int aSize)
Definition netclass.h:192
int HasViaDrill() const
Definition netclass.h:165
int GetDiffPairViaGap() const
Definition netclass.h:206
void SetViaDrillParent(NETCLASS *parent)
Definition netclass.h:170
wxString GetTuningProfile() const
Definition netclass.h:271
void SetDiffPairGapParent(NETCLASS *parent)
Definition netclass.h:202
void SetTuningProfileParent(NETCLASS *aParent)
Definition netclass.h:272
int GetDiffPairGap() const
Definition netclass.h:198
int GetuViaDrill() const
Definition netclass.h:182
bool HasViaDiameter() const
Definition netclass.h:157
int GetLineStyle() const
Definition netclass.h:259
bool HasDiffPairWidth() const
Definition netclass.h:189
bool HasuViaDiameter() const
Definition netclass.h:173
void SetTrackWidthParent(NETCLASS *parent)
Definition netclass.h:154
int GetuViaDiameter() const
Definition netclass.h:174
bool HasTrackWidth() const
Definition netclass.h:149
void SetViaDiameterParent(NETCLASS *parent)
Definition netclass.h:162
int GetDiffPairWidth() const
Definition netclass.h:190
void SetuViaDrill(int aSize)
Definition netclass.h:184
int GetWireWidth() const
Definition netclass.h:229
void SetDiffPairGap(int aSize)
Definition netclass.h:200
void SetBusWidthParent(NETCLASS *parent)
Definition netclass.h:241
void SetClearance(int aClearance)
Definition netclass.h:144
COLOR4D GetPcbColor(bool aIsForSave=false) const
Definition netclass.h:214
bool HasDiffPairGap() const
Definition netclass.h:197
COLOR4D GetSchematicColor(bool aIsForSave=false) const
Definition netclass.h:244
void SetBusWidth(int aWidth)
Definition netclass.h:239
void SetClearanceParent(NETCLASS *parent)
Definition netclass.h:146
int GetTrackWidth() const
Definition netclass.h:150
void SetWireWidth(int aWidth)
Definition netclass.h:231
void SetTuningProfile(const wxString &aTuningProfile)
Definition netclass.h:270
bool HasTuningProfile() const
Definition netclass.h:269
bool HasWireWidth() const
Definition netclass.h:228
int GetClearance() const
Definition netclass.h:142
void SetuViaDiameterParent(NETCLASS *parent)
Definition netclass.h:178
void SetTrackWidth(int aWidth)
Definition netclass.h:152
bool HasDiffPairViaGap() const
Definition netclass.h:205
int GetBusWidth() const
Definition netclass.h:237
bool HasClearance() const
Definition netclass.h:141
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 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...
void makeEffectiveNetclass(std::shared_ptr< NETCLASS > &effectiveNetclass, std::vector< std::shared_ptr< NETCLASS > > &netclasses) const
Creates an effective aggregate netclass from the given constituent netclasses.
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:481
#define _(s)
@ CTX_NETCLASS
nlohmann::json json
Definition gerbview.cpp:50
std::string source
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