KiCad PCB EDA Suite
Loading...
Searching...
No Matches
connection_graph.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) 2018 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Jon Evans <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include <algorithm>
24#include <list>
25#include <future>
26#include <vector>
27#include <unordered_map>
28#include <app_monitor.h>
29#include <core/profile.h>
30#include <core/kicad_algo.h>
31#include <common.h>
32#include <erc/erc.h>
33#include <pin_type.h>
34#include <sch_bus_entry.h>
35#include <sch_symbol.h>
36#include <sch_edit_frame.h>
37#include <sch_line.h>
38#include <sch_marker.h>
39#include <sch_pin.h>
40#include <sch_rule_area.h>
41#include <sch_sheet.h>
42#include <sch_sheet_path.h>
43#include <sch_sheet_pin.h>
44#include <sch_text.h>
45#include <schematic.h>
46#include <symbol.h>
47#include <connection_graph.h>
50#include <widgets/ui_common.h>
51#include <string_utils.h>
52#include <thread_pool.h>
53#include <wx/log.h>
54
55#include <advanced_config.h> // for realtime connectivity switch in release builds
56
57
62static const wxChar DanglingProfileMask[] = wxT( "CONN_PROFILE" );
63
64
69static const wxChar ConnTrace[] = wxT( "CONN" );
70
71
73{
74 m_items.erase( aItem );
75 m_drivers.erase( aItem );
76
77 if( aItem == m_driver )
78 {
79 m_driver = nullptr;
80 m_driver_connection = nullptr;
81 }
82
83 if( aItem->Type() == SCH_SHEET_PIN_T )
84 m_hier_pins.erase( static_cast<SCH_SHEET_PIN*>( aItem ) );
85
86 if( aItem->Type() == SCH_HIER_LABEL_T )
87 m_hier_ports.erase( static_cast<SCH_HIERLABEL*>( aItem ) );
88}
89
90
92{
93 m_items.erase( aOldItem );
94 m_items.insert( aNewItem );
95
96 m_drivers.erase( aOldItem );
97 m_drivers.insert( aNewItem );
98
99 if( aOldItem == m_driver )
100 {
101 m_driver = aNewItem;
103 }
104
105 SCH_CONNECTION* old_conn = aOldItem->Connection( &m_sheet );
106 SCH_CONNECTION* new_conn = aNewItem->GetOrInitConnection( m_sheet, m_graph );
107
108 if( old_conn && new_conn )
109 {
110 new_conn->Clone( *old_conn );
111
112 if( old_conn->IsDriver() )
113 new_conn->SetDriver( aNewItem );
114
115 new_conn->ClearDirty();
116 }
117
118 if( aOldItem->Type() == SCH_SHEET_PIN_T )
119 {
120 m_hier_pins.erase( static_cast<SCH_SHEET_PIN*>( aOldItem ) );
121 m_hier_pins.insert( static_cast<SCH_SHEET_PIN*>( aNewItem ) );
122 }
123
124 if( aOldItem->Type() == SCH_HIER_LABEL_T )
125 {
126 m_hier_ports.erase( static_cast<SCH_HIERLABEL*>( aOldItem ) );
127 m_hier_ports.insert( static_cast<SCH_HIERLABEL*>( aNewItem ) );
128 }
129}
130
131
132bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCheckMultipleDrivers )
133{
134 std::lock_guard lock( m_driver_mutex );
135
136 // Collect candidate drivers of highest priority in a simple vector which will be
137 // sorted later. Using a vector makes the ranking logic explicit and easier to
138 // maintain than relying on the ordering semantics of std::set.
139 PRIORITY highest_priority = PRIORITY::INVALID;
140 std::vector<SCH_ITEM*> candidates;
141 std::set<SCH_ITEM*> strong_drivers;
142
143 m_driver = nullptr;
144
145 // Hierarchical labels are lower priority than local labels here,
146 // because on the first pass we want local labels to drive subgraphs
147 // so that we can identify same-sheet neighbors and link them together.
148 // Hierarchical labels will end up overriding the final net name if
149 // a higher-level sheet has a different name during the hierarchical
150 // pass.
151
152 for( SCH_ITEM* item : m_drivers )
153 {
154 PRIORITY item_priority = GetDriverPriority( item );
155
156 if( item_priority == PRIORITY::PIN )
157 {
158 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
159
160 if( !static_cast<SCH_SYMBOL*>( pin->GetParentSymbol() )->IsInNetlist() )
161 continue;
162 }
163
164 if( item_priority >= PRIORITY::HIER_LABEL )
165 strong_drivers.insert( item );
166
167 if( item_priority > highest_priority )
168 {
169 candidates.clear();
170 candidates.push_back( item );
171 highest_priority = item_priority;
172 }
173 else if( !candidates.empty() && ( item_priority == highest_priority ) )
174 {
175 candidates.push_back( item );
176 }
177 }
178
179 if( highest_priority >= PRIORITY::HIER_LABEL )
180 m_strong_driver = true;
181
182 // Power pins are 5, global labels are 6
183 m_local_driver = ( highest_priority < PRIORITY::GLOBAL_POWER_PIN );
184
185 if( !candidates.empty() )
186 {
187 // Candidates are ranked using the following rules (in order):
188 // 1. Prefer bus supersets over subsets to keep the widest connection.
189 // 2. Prefer pins on power symbols (global first, then local) over regular pins.
190 // 3. Prefer output sheet pins over input sheet pins.
191 // 4. Prefer names that do not look auto-generated (avoid "-Pad" suffixes).
192 // 5. Fall back to alphabetical comparison for deterministic ordering.
193 auto candidate_cmp = [&]( SCH_ITEM* a, SCH_ITEM* b )
194 {
195 SCH_CONNECTION* ac = a->Connection( &m_sheet );
196 SCH_CONNECTION* bc = b->Connection( &m_sheet );
197
198 if( ac->IsBus() && bc->IsBus() )
199 {
200 if( bc->IsSubsetOf( ac ) && !ac->IsSubsetOf( bc ) )
201 {
202 return true;
203 }
204
205 if( !bc->IsSubsetOf( ac ) && ac->IsSubsetOf( bc ) )
206 {
207 return false;
208 }
209 }
210
211 if( a->Type() == SCH_PIN_T && b->Type() == SCH_PIN_T )
212 {
213 SCH_PIN* pa = static_cast<SCH_PIN*>( a );
214 SCH_PIN* pb = static_cast<SCH_PIN*>( b );
215
216 SYMBOL* aParent = pa->GetLibPin() ? pa->GetLibPin()->GetParentSymbol() : nullptr;
217 SYMBOL* bParent = pb->GetLibPin() ? pb->GetLibPin()->GetParentSymbol() : nullptr;
218
219 bool aGlobal = aParent && aParent->IsGlobalPower();
220 bool bGlobal = bParent && bParent->IsGlobalPower();
221
222 if( aGlobal != bGlobal )
223 return aGlobal;
224
225 bool aLocal = aParent && aParent->IsLocalPower();
226 bool bLocal = bParent && bParent->IsLocalPower();
227
228 if( aLocal != bLocal )
229 return aLocal;
230 }
231
232 if( a->Type() == SCH_SHEET_PIN_T && b->Type() == SCH_SHEET_PIN_T )
233 {
234 SCH_SHEET_PIN* sa = static_cast<SCH_SHEET_PIN*>( a );
235 SCH_SHEET_PIN* sb = static_cast<SCH_SHEET_PIN*>( b );
236
237 if( sa->GetShape() != sb->GetShape() )
238 {
240 return true;
242 return false;
243 }
244 }
245
246 const wxString& a_name = GetNameForDriver( a );
247 const wxString& b_name = GetNameForDriver( b );
248
249 bool a_lowQualityName = a_name.Contains( "-Pad" );
250 bool b_lowQualityName = b_name.Contains( "-Pad" );
251
252 if( a_lowQualityName != b_lowQualityName )
253 return !a_lowQualityName;
254
255 return a_name < b_name;
256 };
257
258 std::sort( candidates.begin(), candidates.end(), candidate_cmp );
259
260 m_driver = candidates.front();
261 }
262
263 if( strong_drivers.size() > 1 )
264 m_multiple_drivers = true;
265
266 // Drop weak drivers
267 if( m_strong_driver )
268 {
269 m_drivers.clear();
270 m_drivers.insert( strong_drivers.begin(), strong_drivers.end() );
271 }
272
273 // Cache driver connection
274 if( m_driver )
275 {
276 m_driver_connection = m_driver->Connection( &m_sheet );
277 m_driver_connection->ConfigureFromLabel( GetNameForDriver( m_driver ) );
278 m_driver_connection->SetDriver( m_driver );
279 m_driver_connection->ClearDirty();
280 }
281 else if( !m_is_bus_member )
282 {
283 m_driver_connection = nullptr;
284 }
285
286 return ( m_driver != nullptr );
287}
288
289
291 SCH_ITEM*>>& aItems,
292 std::set<CONNECTION_SUBGRAPH*>& aSubgraphs )
293{
294 CONNECTION_SUBGRAPH* sg = this;
295
296 while( sg->m_absorbed_by )
297 {
298 wxCHECK2( sg->m_graph == sg->m_absorbed_by->m_graph, continue );
299 sg = sg->m_absorbed_by;
300 }
301
302 // If we are unable to insert the subgraph into the set, then we have already
303 // visited it and don't need to add it again.
304 if( aSubgraphs.insert( sg ).second == false )
305 return;
306
307 aSubgraphs.insert( sg->m_absorbed_subgraphs.begin(), sg->m_absorbed_subgraphs.end() );
308
309 for( SCH_ITEM* item : sg->m_items )
310 aItems.emplace( m_sheet, item );
311
312 for( CONNECTION_SUBGRAPH* child_sg : sg->m_hier_children )
313 child_sg->getAllConnectedItems( aItems, aSubgraphs );
314}
315
316
318{
319 if( !m_driver || m_dirty )
320 return "";
321
322 if( !m_driver->Connection( &m_sheet ) )
323 {
324#ifdef CONNECTIVITY_DEBUG
325 wxASSERT_MSG( false, wxS( "Tried to get the net name of an item with no connection" ) );
326#endif
327
328 return "";
329 }
330
331 return m_driver->Connection( &m_sheet )->Name();
332}
333
334
335std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetAllBusLabels() const
336{
337 std::vector<SCH_ITEM*> labels;
338
339 for( SCH_ITEM* item : m_drivers )
340 {
341 switch( item->Type() )
342 {
343 case SCH_LABEL_T:
345 case SCH_HIER_LABEL_T:
346 {
347 CONNECTION_TYPE type = item->Connection( &m_sheet )->Type();
348
349 // Only consider bus vectors
350 if( type == CONNECTION_TYPE::BUS || type == CONNECTION_TYPE::BUS_GROUP )
351 labels.push_back( item );
352
353 break;
354 }
355
356 default:
357 break;
358 }
359 }
360
361 return labels;
362}
363
364
365std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetVectorBusLabels() const
366{
367 std::vector<SCH_ITEM*> labels;
368
369 for( SCH_ITEM* item : m_drivers )
370 {
371 switch( item->Type() )
372 {
373 case SCH_LABEL_T:
375 case SCH_HIER_LABEL_T:
376 {
377 SCH_CONNECTION* label_conn = item->Connection( &m_sheet );
378
379 // Only consider bus vectors
380 if( label_conn->Type() == CONNECTION_TYPE::BUS )
381 labels.push_back( item );
382
383 break;
384 }
385
386 default:
387 break;
388 }
389 }
390
391 return labels;
392}
393
394
396{
397 switch( aItem->Type() )
398 {
399 case SCH_PIN_T:
400 {
401 SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
402 bool forceNoConnect = m_no_connect != nullptr;
403
404 return pin->GetDefaultNetName( m_sheet, forceNoConnect );
405 }
406
407 case SCH_LABEL_T:
409 case SCH_HIER_LABEL_T:
410 {
411 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
412
413 // NB: any changes here will need corresponding changes in SCH_LABEL_BASE::cacheShownText()
414 return EscapeString( label->GetShownText( &m_sheet, false ), CTX_NETNAME );
415 }
416
417 case SCH_SHEET_PIN_T:
418 {
419 // Sheet pins need to use their parent sheet as their starting sheet or they will resolve
420 // variables on the current sheet first
421 SCH_SHEET_PIN* sheetPin = static_cast<SCH_SHEET_PIN*>( aItem );
423
424 if( path.Last() != sheetPin->GetParent() )
425 path.push_back( sheetPin->GetParent() );
426
427 return EscapeString( sheetPin->GetShownText( &path, false ), CTX_NETNAME );
428 }
429
430 default:
431 wxFAIL_MSG( wxS( "Unhandled item type in GetNameForDriver" ) );
432 return wxEmptyString;
433 }
434}
435
436
437const wxString& CONNECTION_SUBGRAPH::GetNameForDriver( SCH_ITEM* aItem ) const
438{
439 if( aItem->HasCachedDriverName() )
440 return aItem->GetCachedDriverName();
441
442 std::lock_guard lock( m_driver_name_cache_mutex );
443 auto it = m_driver_name_cache.find( aItem );
444
445 if( it != m_driver_name_cache.end() )
446 return it->second;
447
448 return m_driver_name_cache.emplace( aItem, driverName( aItem ) ).first->second;
449}
450
451
452const std::vector<std::pair<wxString, SCH_ITEM*>>
454{
455 std::vector<std::pair<wxString, SCH_ITEM*>> foundNetclasses;
456
457 const std::unordered_set<SCH_RULE_AREA*>& ruleAreaCache = aItem->GetRuleAreaCache();
458
459 // Get netclasses on attached rule areas
460 for( SCH_RULE_AREA* ruleArea : ruleAreaCache )
461 {
462 const std::vector<std::pair<wxString, SCH_ITEM*>> ruleAreaNetclasses =
463 ruleArea->GetResolvedNetclasses( &m_sheet );
464
465 if( ruleAreaNetclasses.size() > 0 )
466 {
467 foundNetclasses.insert( foundNetclasses.end(), ruleAreaNetclasses.begin(),
468 ruleAreaNetclasses.end() );
469 }
470 }
471
472 // Get netclasses on child fields
473 aItem->RunOnChildren(
474 [&]( SCH_ITEM* aChild )
475 {
476 if( aChild->Type() == SCH_FIELD_T )
477 {
478 SCH_FIELD* field = static_cast<SCH_FIELD*>( aChild );
479
480 if( field->GetCanonicalName() == wxT( "Netclass" ) )
481 {
482 wxString netclass = field->GetShownText( &m_sheet, false );
483
484 if( netclass != wxEmptyString )
485 foundNetclasses.push_back( { netclass, aItem } );
486 }
487 }
488 },
490
491 std::sort(
492 foundNetclasses.begin(), foundNetclasses.end(),
493 []( const std::pair<wxString, SCH_ITEM*>& i1, const std::pair<wxString, SCH_ITEM*>& i2 )
494 {
495 return i1.first < i2.first;
496 } );
497
498 return foundNetclasses;
499}
500
501
503{
504 wxCHECK( m_sheet == aOther->m_sheet, /* void */ );
505
506 for( SCH_ITEM* item : aOther->m_items )
507 {
509 AddItem( item );
510 }
511
512 m_absorbed_subgraphs.insert( aOther );
513 m_absorbed_subgraphs.insert( aOther->m_absorbed_subgraphs.begin(),
514 aOther->m_absorbed_subgraphs.end() );
515
516 m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() );
517 m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() );
518
520
521 std::function<void( CONNECTION_SUBGRAPH* )> set_absorbed_by =
522 [ & ]( CONNECTION_SUBGRAPH *child )
523 {
524 child->m_absorbed_by = this;
525
526 for( CONNECTION_SUBGRAPH* subchild : child->m_absorbed_subgraphs )
527 set_absorbed_by( subchild );
528 };
529
530 aOther->m_absorbed = true;
531 aOther->m_dirty = false;
532 aOther->m_driver = nullptr;
533 aOther->m_driver_connection = nullptr;
534
535 set_absorbed_by( aOther );
536}
537
538
540{
541 m_items.insert( aItem );
542
543 if( aItem->Connection( &m_sheet )->IsDriver() )
544 m_drivers.insert( aItem );
545
546 if( aItem->Type() == SCH_SHEET_PIN_T )
547 m_hier_pins.insert( static_cast<SCH_SHEET_PIN*>( aItem ) );
548 else if( aItem->Type() == SCH_HIER_LABEL_T )
549 m_hier_ports.insert( static_cast<SCH_HIERLABEL*>( aItem ) );
550}
551
552
554{
556 return;
557
558 for( SCH_ITEM* item : m_items )
559 {
560 SCH_CONNECTION* item_conn = item->GetOrInitConnection( m_sheet, m_graph );
561
562 if( !item_conn )
563 continue;
564
565 if( ( m_driver_connection->IsBus() && item_conn->IsNet() ) ||
566 ( m_driver_connection->IsNet() && item_conn->IsBus() ) )
567 {
568 continue;
569 }
570
571 if( item != m_driver )
572 {
573 item_conn->Clone( *m_driver_connection );
574 item_conn->ClearDirty();
575 }
576 }
577}
578
579
581{
582 if( !aDriver )
583 return PRIORITY::NONE;
584
585 auto libSymbolRef =
586 []( const SCH_SYMBOL* symbol ) -> wxString
587 {
588 if( const std::unique_ptr<LIB_SYMBOL>& part = symbol->GetLibSymbolRef() )
589 return part->GetReferenceField().GetText();
590
591 return wxEmptyString;
592 };
593
594 switch( aDriver->Type() )
595 {
600
601 case SCH_PIN_T:
602 {
603 SCH_PIN* sch_pin = static_cast<SCH_PIN*>( aDriver );
604 const SCH_SYMBOL* sym = static_cast<SCH_SYMBOL*>( sch_pin->GetParentSymbol() );
605
606 if( sch_pin->IsGlobalPower() )
608 else if( sch_pin->IsLocalPower() )
610 else if( !sym || sym->GetExcludedFromBoard() || libSymbolRef( sym ).StartsWith( '#' ) )
611 return PRIORITY::NONE;
612 else
613 return PRIORITY::PIN;
614 }
615
616 default:
617 return PRIORITY::NONE;
618 }
619}
620
621
623{
624 std::copy( aGraph.m_items.begin(), aGraph.m_items.end(),
625 std::back_inserter( m_items ) );
626
627 for( SCH_ITEM* item : aGraph.m_items )
628 item->SetConnectionGraph( this );
629
630 std::copy( aGraph.m_subgraphs.begin(), aGraph.m_subgraphs.end(),
631 std::back_inserter( m_subgraphs ) );
632
633 for( CONNECTION_SUBGRAPH* sg : aGraph.m_subgraphs )
634 {
635 if( sg->m_driver_connection )
636 sg->m_driver_connection->SetGraph( this );
637
638 sg->m_graph = this;
639 }
640
641 std::copy( aGraph.m_driver_subgraphs.begin(), aGraph.m_driver_subgraphs.end(),
642 std::back_inserter( m_driver_subgraphs ) );
643
644 std::copy( aGraph.m_global_power_pins.begin(), aGraph.m_global_power_pins.end(),
645 std::back_inserter( m_global_power_pins ) );
646
647 for( auto& [key, value] : aGraph.m_net_name_to_subgraphs_map )
648 m_net_name_to_subgraphs_map.insert_or_assign( key, value );
649
650 for( auto& [key, value] : aGraph.m_sheet_to_subgraphs_map )
651 m_sheet_to_subgraphs_map.insert_or_assign( key, value );
652
653 for( auto& [key, value] : aGraph.m_net_name_to_code_map )
654 m_net_name_to_code_map.insert_or_assign( key, value );
655
656 for( auto& [key, value] : aGraph.m_bus_name_to_code_map )
657 m_bus_name_to_code_map.insert_or_assign( key, value );
658
659 for( auto& [key, value] : aGraph.m_net_code_to_subgraphs_map )
660 m_net_code_to_subgraphs_map.insert_or_assign( key, value );
661
662 for( auto& [key, value] : aGraph.m_item_to_subgraph_map )
663 m_item_to_subgraph_map.insert_or_assign( key, value );
664
665 for( auto& [key, value] : aGraph.m_local_label_cache )
666 m_local_label_cache.insert_or_assign( key, value );
667
668 for( auto& [key, value] : aGraph.m_global_label_cache )
669 m_global_label_cache.insert_or_assign( key, value );
670
671 m_last_bus_code = std::max( m_last_bus_code, aGraph.m_last_bus_code );
672 m_last_net_code = std::max( m_last_net_code, aGraph.m_last_net_code );
674
675}
676
677
679{
680 wxCHECK2( aOldItem->Type() == aNewItem->Type(), return );
681
682 auto exchange = [&]( SCH_ITEM* aOld, SCH_ITEM* aNew )
683 {
684 auto it = m_item_to_subgraph_map.find( aOld );
685
686 if( it == m_item_to_subgraph_map.end() )
687 return;
688
689 CONNECTION_SUBGRAPH* sg = it->second;
690
691 sg->ExchangeItem( aOld, aNew );
692
693 m_item_to_subgraph_map.erase( it );
694 m_item_to_subgraph_map.emplace( aNew, sg );
695
696 for( auto it2 = m_items.begin(); it2 != m_items.end(); ++it2 )
697 {
698 if( *it2 == aOld )
699 {
700 *it2 = aNew;
701 break;
702 }
703 }
704 };
705
706 exchange( aOldItem, aNewItem );
707
708 if( aOldItem->Type() == SCH_SYMBOL_T )
709 {
710 SCH_SYMBOL* oldSymbol = static_cast<SCH_SYMBOL*>( aOldItem );
711 SCH_SYMBOL* newSymbol = static_cast<SCH_SYMBOL*>( aNewItem );
712 std::vector<SCH_PIN*> oldPins = oldSymbol->GetPins( &m_schematic->CurrentSheet() );
713 std::vector<SCH_PIN*> newPins = newSymbol->GetPins( &m_schematic->CurrentSheet() );
714
715 wxCHECK2( oldPins.size() == newPins.size(), return );
716
717 for( size_t ii = 0; ii < oldPins.size(); ii++ )
718 {
719 exchange( oldPins[ii], newPins[ii] );
720 }
721 }
722}
723
724
726{
727 for( auto& subgraph : m_subgraphs )
728 {
730 if( subgraph->m_graph == this )
731 delete subgraph;
732 }
733
734 m_items.clear();
735 m_subgraphs.clear();
736 m_driver_subgraphs.clear();
738 m_global_power_pins.clear();
739 m_bus_alias_cache.clear();
745 m_local_label_cache.clear();
746 m_global_label_cache.clear();
747 m_last_net_code = 1;
748 m_last_bus_code = 1;
750}
751
752
753void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnconditional,
754 std::function<void( SCH_ITEM* )>* aChangedItemHandler,
755 PROGRESS_REPORTER* aProgressReporter )
756{
757 APP_MONITOR::TRANSACTION monitorTrans( "CONNECTION_GRAPH::Recalculate", "Recalculate" );
758 PROF_TIMER recalc_time( "CONNECTION_GRAPH::Recalculate" );
759 monitorTrans.Start();
760
761 if( aUnconditional )
762 Reset();
763
764 monitorTrans.StartSpan( "updateItemConnectivity", "" );
765 PROF_TIMER update_items( "updateItemConnectivity" );
766
767 m_sheetList = aSheetList;
768 std::set<SCH_ITEM*> dirty_items;
769
770 int count = aSheetList.size() * 2;
771 int done = 0;
772
773 for( const SCH_SHEET_PATH& sheet : aSheetList )
774 {
775 if( aProgressReporter )
776 {
777 aProgressReporter->SetCurrentProgress( done++ / (double) count );
778 aProgressReporter->KeepRefreshing();
779 }
780
781 std::vector<SCH_ITEM*> items;
782
783 // Store current unit value, to replace it after calculations
784 std::vector<std::pair<SCH_SYMBOL*, int>> symbolsChanged;
785
786 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
787 {
788 if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
789 {
790 wxLogTrace( ConnTrace, wxT( "Adding item %s to connectivity graph update" ),
791 item->GetTypeDesc() );
792 items.push_back( item );
793 dirty_items.insert( item );
794
795 // Add any symbol dirty pins to the dirty_items list
796 if( item->Type() == SCH_SYMBOL_T )
797 {
798 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
799
800 for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
801 {
802 if( pin->IsConnectivityDirty() )
803 {
804 dirty_items.insert( pin );
805 }
806 }
807 }
808 }
809 // If the symbol isn't dirty, look at the pins
810 // TODO: remove symbols from connectivity graph and only use pins
811 else if( item->Type() == SCH_SYMBOL_T )
812 {
813 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
814
815 for( SCH_PIN* pin : symbol->GetPins( &sheet ) )
816 {
817 if( pin->IsConnectivityDirty() )
818 {
819 items.push_back( pin );
820 dirty_items.insert( pin );
821 }
822 }
823 }
824 else if( item->Type() == SCH_SHEET_T )
825 {
826 SCH_SHEET* sheetItem = static_cast<SCH_SHEET*>( item );
827
828 for( SCH_SHEET_PIN* pin : sheetItem->GetPins() )
829 {
830 if( pin->IsConnectivityDirty() )
831 {
832 items.push_back( pin );
833 dirty_items.insert( pin );
834 }
835 }
836 }
837
838 // Ensure the hierarchy info stored in the SCH_SCREEN (such as symbol units) reflects
839 // the current SCH_SHEET_PATH
840 if( item->Type() == SCH_SYMBOL_T )
841 {
842 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
843 int new_unit = symbol->GetUnitSelection( &sheet );
844
845 // Store the initial unit value so we can restore it after calculations
846 if( symbol->GetUnit() != new_unit )
847 symbolsChanged.push_back( { symbol, symbol->GetUnit() } );
848
849 symbol->SetUnit( new_unit );
850 }
851 }
852
853 m_items.reserve( m_items.size() + items.size() );
854
855 updateItemConnectivity( sheet, items );
856
857 if( aProgressReporter )
858 {
859 aProgressReporter->SetCurrentProgress( done++ / count );
860 aProgressReporter->KeepRefreshing();
861 }
862
863 // UpdateDanglingState() also adds connected items for SCH_TEXT
864 sheet.LastScreen()->TestDanglingEnds( &sheet, aChangedItemHandler );
865
866 // Restore the m_unit member variables where we had to change them
867 for( const auto& [ symbol, originalUnit ] : symbolsChanged )
868 symbol->SetUnit( originalUnit );
869 }
870
871 // Restore the dangling states of items in the current SCH_SCREEN to match the current
872 // SCH_SHEET_PATH.
873 SCH_SCREEN* currentScreen = m_schematic->CurrentSheet().LastScreen();
874
875 if( currentScreen )
876 currentScreen->TestDanglingEnds( &m_schematic->CurrentSheet(), aChangedItemHandler );
877
878 for( SCH_ITEM* item : dirty_items )
879 item->SetConnectivityDirty( false );
880
881
882 monitorTrans.FinishSpan();
883 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
884 update_items.Show();
885
886 PROF_TIMER build_graph( "buildConnectionGraph" );
887 monitorTrans.StartSpan( "BuildConnectionGraph", "" );
888
889 buildConnectionGraph( aChangedItemHandler, aUnconditional );
890
891 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
892 build_graph.Show();
893
894 monitorTrans.FinishSpan();
895
896 recalc_time.Stop();
897
898 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
899 recalc_time.Show();
900
901 monitorTrans.Finish();
902}
903
904
905std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> CONNECTION_GRAPH::ExtractAffectedItems(
906 const std::set<SCH_ITEM*> &aItems )
907{
908 std::set<std::pair<SCH_SHEET_PATH, SCH_ITEM*>> retvals;
909 std::set<CONNECTION_SUBGRAPH*> subgraphs;
910
911 auto traverse_subgraph = [&retvals, &subgraphs]( CONNECTION_SUBGRAPH* aSubgraph )
912 {
913 // Find the primary subgraph on this sheet
914 while( aSubgraph->m_absorbed_by )
915 {
916 // Should we skip this if the absorbed by sub-graph is not this sub-grap?
917 wxASSERT( aSubgraph->m_graph == aSubgraph->m_absorbed_by->m_graph );
918 aSubgraph = aSubgraph->m_absorbed_by;
919 }
920
921 // Find the top most connected subgraph on all sheets
922 while( aSubgraph->m_hier_parent )
923 {
924 // Should we skip this if the absorbed by sub-graph is not this sub-grap?
925 wxASSERT( aSubgraph->m_graph == aSubgraph->m_hier_parent->m_graph );
926 aSubgraph = aSubgraph->m_hier_parent;
927 }
928
929 // Recurse through all subsheets to collect connected items
930 aSubgraph->getAllConnectedItems( retvals, subgraphs );
931 };
932
933 auto extract_element = [&]( SCH_ITEM* aItem )
934 {
935 CONNECTION_SUBGRAPH* item_sg = GetSubgraphForItem( aItem );
936
937 if( !item_sg )
938 {
939 wxLogTrace( ConnTrace, wxT( "Item %s not found in connection graph" ),
940 aItem->GetTypeDesc() );
941 return;
942 }
943
944 if( !item_sg->ResolveDrivers( true ) )
945 {
946 wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld (%p) has no driver" ),
947 aItem->GetTypeDesc(), item_sg->m_code, item_sg );
948 }
949
950 std::vector<CONNECTION_SUBGRAPH*> sg_to_scan = GetAllSubgraphs( item_sg->GetNetName() );
951
952 if( sg_to_scan.empty() )
953 {
954 wxLogTrace( ConnTrace, wxT( "Item %s in subgraph %ld with net %s has no neighbors" ),
955 aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName() );
956 sg_to_scan.push_back( item_sg );
957 }
958
959 wxLogTrace( ConnTrace,
960 wxT( "Removing all item %s connections from subgraph %ld with net %s: Found "
961 "%zu subgraphs" ),
962 aItem->GetTypeDesc(), item_sg->m_code, item_sg->GetNetName(),
963 sg_to_scan.size() );
964
965 for( CONNECTION_SUBGRAPH* sg : sg_to_scan )
966 {
967 traverse_subgraph( sg );
968
969 for( auto& bus_it : sg->m_bus_neighbors )
970 {
971 for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
972 traverse_subgraph( bus_sg );
973 }
974
975 for( auto& bus_it : sg->m_bus_parents )
976 {
977 for( CONNECTION_SUBGRAPH* bus_sg : bus_it.second )
978 traverse_subgraph( bus_sg );
979 }
980 }
981
982 std::erase( m_items, aItem );
983 };
984
985 for( SCH_ITEM* item : aItems )
986 {
987 if( item->Type() == SCH_SHEET_T )
988 {
989 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
990
991 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
992 extract_element( pin );
993 }
994 else if ( item->Type() == SCH_SYMBOL_T )
995 {
996 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
997
998 for( SCH_PIN* pin : symbol->GetPins( &m_schematic->CurrentSheet() ) )
999 extract_element( pin );
1000 }
1001 else
1002 {
1003 extract_element( item );
1004 }
1005 }
1006
1007 removeSubgraphs( subgraphs );
1008
1009 for( const auto& [path, item] : retvals )
1010 std::erase( m_items, item );
1011
1012 return retvals;
1013}
1014
1015
1017{
1018 auto it = m_item_to_subgraph_map.find( aItem );
1019
1020 if( it == m_item_to_subgraph_map.end() )
1021 return;
1022
1023 CONNECTION_SUBGRAPH* subgraph = it->second;
1024
1025 while(subgraph->m_absorbed_by )
1026 subgraph = subgraph->m_absorbed_by;
1027
1028 subgraph->RemoveItem( aItem );
1029 std::erase( m_items, aItem );
1030 m_item_to_subgraph_map.erase( it );
1031}
1032
1033
1034void CONNECTION_GRAPH::removeSubgraphs( std::set<CONNECTION_SUBGRAPH*>& aSubgraphs )
1035{
1036 wxLogTrace( ConnTrace, wxT( "Removing %zu subgraphs" ), aSubgraphs.size() );
1037 std::sort( m_driver_subgraphs.begin(), m_driver_subgraphs.end() );
1038 std::sort( m_subgraphs.begin(), m_subgraphs.end() );
1039 std::set<int> codes_to_remove;
1040
1041 for( auto& el : m_sheet_to_subgraphs_map )
1042 {
1043 std::sort( el.second.begin(), el.second.end() );
1044 }
1045
1046 for( CONNECTION_SUBGRAPH* sg : aSubgraphs )
1047 {
1048 for( auto& it : sg->m_bus_neighbors )
1049 {
1050 for( CONNECTION_SUBGRAPH* neighbor : it.second )
1051 {
1052 auto& parents = neighbor->m_bus_parents[it.first];
1053
1054 for( auto test = parents.begin(); test != parents.end(); )
1055 {
1056 if( *test == sg )
1057 test = parents.erase( test );
1058 else
1059 ++test;
1060 }
1061
1062 if( parents.empty() )
1063 neighbor->m_bus_parents.erase( it.first );
1064 }
1065 }
1066
1067 for( auto& it : sg->m_bus_parents )
1068 {
1069 for( CONNECTION_SUBGRAPH* parent : it.second )
1070 {
1071 auto& neighbors = parent->m_bus_neighbors[it.first];
1072
1073 for( auto test = neighbors.begin(); test != neighbors.end(); )
1074 {
1075 if( *test == sg )
1076 test = neighbors.erase( test );
1077 else
1078 ++test;
1079 }
1080
1081 if( neighbors.empty() )
1082 parent->m_bus_neighbors.erase( it.first );
1083 }
1084 }
1085
1086 {
1087 auto it = std::lower_bound( m_driver_subgraphs.begin(), m_driver_subgraphs.end(), sg );
1088
1089 while( it != m_driver_subgraphs.end() && *it == sg )
1090 it = m_driver_subgraphs.erase( it );
1091 }
1092
1093 {
1094 auto it = std::lower_bound( m_subgraphs.begin(), m_subgraphs.end(), sg );
1095
1096 while( it != m_subgraphs.end() && *it == sg )
1097 it = m_subgraphs.erase( it );
1098 }
1099
1100 for( auto& el : m_sheet_to_subgraphs_map )
1101 {
1102 auto it = std::lower_bound( el.second.begin(), el.second.end(), sg );
1103
1104 while( it != el.second.end() && *it == sg )
1105 it = el.second.erase( it );
1106 }
1107
1108 auto remove_sg = [sg]( auto it ) -> bool
1109 {
1110 for( const CONNECTION_SUBGRAPH* test_sg : it->second )
1111 {
1112 if( sg == test_sg )
1113 return true;
1114 }
1115
1116 return false;
1117 };
1118
1119 for( auto it = m_global_label_cache.begin(); it != m_global_label_cache.end(); )
1120 {
1121 if( remove_sg( it ) )
1122 it = m_global_label_cache.erase( it );
1123 else
1124 ++it;
1125 }
1126
1127 for( auto it = m_local_label_cache.begin(); it != m_local_label_cache.end(); )
1128 {
1129 if( remove_sg( it ) )
1130 it = m_local_label_cache.erase( it );
1131 else
1132 ++it;
1133 }
1134
1135 for( auto it = m_net_code_to_subgraphs_map.begin();
1136 it != m_net_code_to_subgraphs_map.end(); )
1137 {
1138 if( remove_sg( it ) )
1139 {
1140 codes_to_remove.insert( it->first.Netcode );
1141 it = m_net_code_to_subgraphs_map.erase( it );
1142 }
1143 else
1144 {
1145 ++it;
1146 }
1147 }
1148
1149 for( auto it = m_net_name_to_subgraphs_map.begin();
1150 it != m_net_name_to_subgraphs_map.end(); )
1151 {
1152 if( remove_sg( it ) )
1153 it = m_net_name_to_subgraphs_map.erase( it );
1154 else
1155 ++it;
1156 }
1157
1158 for( auto it = m_item_to_subgraph_map.begin(); it != m_item_to_subgraph_map.end(); )
1159 {
1160 if( it->second == sg )
1161 it = m_item_to_subgraph_map.erase( it );
1162 else
1163 ++it;
1164 }
1165
1166
1167 }
1168
1169 for( auto it = m_net_name_to_code_map.begin(); it != m_net_name_to_code_map.end(); )
1170 {
1171 if( codes_to_remove.contains( it->second ) )
1172 it = m_net_name_to_code_map.erase( it );
1173 else
1174 ++it;
1175 }
1176
1177 for( auto it = m_bus_name_to_code_map.begin(); it != m_bus_name_to_code_map.end(); )
1178 {
1179 if( codes_to_remove.contains( it->second ) )
1180 it = m_bus_name_to_code_map.erase( it );
1181 else
1182 ++it;
1183 }
1184
1185 for( CONNECTION_SUBGRAPH* sg : aSubgraphs )
1186 {
1187 sg->m_code = -1;
1188 sg->m_graph = nullptr;
1189 delete sg;
1190 }
1191}
1192
1193
1195 std::map<VECTOR2I, std::vector<SCH_ITEM*>>& aConnectionMap )
1196{
1197 auto updatePin =
1198 [&]( SCH_PIN* aPin, SCH_CONNECTION* aConn )
1199 {
1200 aConn->SetType( CONNECTION_TYPE::NET );
1201 wxString name = aPin->GetDefaultNetName( aSheet );
1202 aPin->ClearConnectedItems( aSheet );
1203
1204 if( aPin->IsGlobalPower() )
1205 {
1206 aConn->SetName( name );
1207 m_global_power_pins.emplace_back( std::make_pair( aSheet, aPin ) );
1208 }
1209 };
1210
1211 std::map<wxString, std::vector<SCH_PIN*>> pinNumberMap;
1212
1213 for( SCH_PIN* pin : aSymbol->GetPins( &aSheet ) )
1214 {
1215 m_items.emplace_back( pin );
1216 SCH_CONNECTION* conn = pin->InitializeConnection( aSheet, this );
1217 updatePin( pin, conn );
1218 aConnectionMap[ pin->GetPosition() ].push_back( pin );
1219 pinNumberMap[pin->GetNumber()].emplace_back( pin );
1220 }
1221
1222 auto linkPinsInVec =
1223 [&]( const std::vector<SCH_PIN*>& aVec )
1224 {
1225 for( size_t i = 0; i < aVec.size(); ++i )
1226 {
1227 for( size_t j = i + 1; j < aVec.size(); ++j )
1228 {
1229 aVec[i]->AddConnectionTo( aSheet, aVec[j] );
1230 aVec[j]->AddConnectionTo( aSheet, aVec[i] );
1231 }
1232 }
1233 };
1234
1235 if( aSymbol->GetLibSymbolRef() )
1236 {
1238 {
1239 for( const auto& [number, group] : pinNumberMap )
1240 linkPinsInVec( group );
1241 }
1242
1243 for( const std::set<wxString>& group : aSymbol->GetLibSymbolRef()->JumperPinGroups() )
1244 {
1245 std::vector<SCH_PIN*> pins;
1246
1247 for( const wxString& pinNumber : group )
1248 {
1249 if( SCH_PIN* pin = aSymbol->GetPin( pinNumber ) )
1250 pins.emplace_back( pin );
1251 }
1252
1253 linkPinsInVec( pins );
1254 }
1255 }
1256}
1257
1258
1260{
1261 aConn->SetType( CONNECTION_TYPE::NET );
1262
1263 // because calling the first time is not thread-safe
1264 wxString name = aPin->GetDefaultNetName( aSheet );
1265 aPin->ClearConnectedItems( aSheet );
1266
1267 if( aPin->IsGlobalPower() )
1268 {
1269 aConn->SetName( name );
1270 m_global_power_pins.emplace_back( std::make_pair( aSheet, aPin ) );
1271 }
1272}
1273
1274
1276 std::map<VECTOR2I, std::vector<SCH_ITEM*>>& aConnectionMap )
1277{
1278 std::vector<VECTOR2I> points = aItem->GetConnectionPoints();
1279 aItem->ClearConnectedItems( aSheet );
1280
1281 m_items.emplace_back( aItem );
1282 SCH_CONNECTION* conn = aItem->InitializeConnection( aSheet, this );
1283
1284 switch( aItem->Type() )
1285 {
1286 case SCH_LINE_T:
1288 break;
1289
1292 static_cast<SCH_BUS_BUS_ENTRY*>( aItem )->m_connected_bus_items[0] = nullptr;
1293 static_cast<SCH_BUS_BUS_ENTRY*>( aItem )->m_connected_bus_items[1] = nullptr;
1294 break;
1295
1296 case SCH_PIN_T:
1297 if( points.empty() )
1298 points = { static_cast<SCH_PIN*>( aItem )->GetPosition() };
1299
1300 updatePinConnectivity( aSheet, static_cast<SCH_PIN*>( aItem ), conn );
1301 break;
1302
1305 static_cast<SCH_BUS_WIRE_ENTRY*>( aItem )->m_connected_bus_item = nullptr;
1306 break;
1307
1308 default: break;
1309 }
1310
1311 for( const VECTOR2I& point : points )
1312 aConnectionMap[point].push_back( aItem );
1313}
1314
1315
1317 const std::vector<SCH_ITEM*>& aItemList )
1318{
1319 wxLogTrace( wxT( "Updating connectivity for sheet %s with %zu items" ),
1320 aSheet.Last()->GetFileName(), aItemList.size() );
1321 std::map<VECTOR2I, std::vector<SCH_ITEM*>> connection_map;
1322
1323 for( SCH_ITEM* item : aItemList )
1324 {
1325 std::vector<VECTOR2I> points = item->GetConnectionPoints();
1326 item->ClearConnectedItems( aSheet );
1327
1328 if( item->Type() == SCH_SHEET_T )
1329 {
1330 for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
1331 {
1332 pin->InitializeConnection( aSheet, this );
1333
1334 pin->ClearConnectedItems( aSheet );
1335
1336 connection_map[ pin->GetTextPos() ].push_back( pin );
1337 m_items.emplace_back( pin );
1338 }
1339 }
1340 else if( item->Type() == SCH_SYMBOL_T )
1341 {
1342 updateSymbolConnectivity( aSheet, static_cast<SCH_SYMBOL*>( item ), connection_map );
1343 }
1344 else
1345 {
1346 updateGenericItemConnectivity( aSheet, item, connection_map );
1347
1351 if( dynamic_cast<SCH_LABEL_BASE*>( item ) )
1352 {
1353 VECTOR2I point = item->GetPosition();
1354 SCH_SCREEN* screen = aSheet.LastScreen();
1355 auto items = screen->Items().Overlapping( point );
1356 std::vector<SCH_ITEM*> overlapping_items;
1357
1358 std::copy_if( items.begin(), items.end(), std::back_inserter( overlapping_items ),
1359 [&]( SCH_ITEM* test_item )
1360 {
1361 return test_item->Type() == SCH_LINE_T
1362 && test_item->HitTest( point, -1 );
1363 } );
1364
1365 // We need at least two connnectable lines that are not the label here
1366 // Otherwise, the label will be normally assigned to one or the other
1367 if( overlapping_items.size() < 2 ) continue;
1368
1369 for( SCH_ITEM* test_item : overlapping_items )
1370 connection_map[point].push_back( test_item );
1371 }
1372
1373 // Junctions connect wires that pass through their position as midpoints.
1374 // This handles schematics where a wire was not split at a junction point,
1375 // which can happen when a wire is placed over an existing junction without
1376 // the schematic topology being updated.
1377 if( item->Type() == SCH_JUNCTION_T )
1378 {
1379 VECTOR2I point = item->GetPosition();
1380 SCH_SCREEN* screen = aSheet.LastScreen();
1381
1382 for( SCH_LINE* wire : screen->GetBusesAndWires( point, true ) )
1383 connection_map[point].push_back( wire );
1384 }
1385 }
1386 }
1387
1388 for( auto& [point, connection_vec] : connection_map )
1389 {
1390 std::sort( connection_vec.begin(), connection_vec.end() );
1391 alg::remove_duplicates( connection_vec );
1392
1393 // Pre-scan to see if we have a bus at this location
1394 SCH_LINE* busLine = aSheet.LastScreen()->GetBus( point );
1395
1396 for( SCH_ITEM* connected_item : connection_vec )
1397 {
1398 // Bus entries are special: they can have connection points in the
1399 // middle of a wire segment, because the junction algo doesn't split
1400 // the segment in two where you place a bus entry. This means that
1401 // bus entries that don't land on the end of a line segment need to
1402 // have "virtual" connection points to the segments they graphically
1403 // touch.
1404 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1405 {
1406 // If this location only has the connection point of the bus
1407 // entry itself, this means that either the bus entry is not
1408 // connected to anything graphically, or that it is connected to
1409 // a segment at some point other than at one of the endpoints.
1410 if( connection_vec.size() == 1 )
1411 {
1412 if( busLine )
1413 {
1414 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1415 bus_entry->m_connected_bus_item = busLine;
1416 }
1417 }
1418 }
1419 // Bus-to-bus entries are treated just like bus wires
1420 else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
1421 {
1422 if( busLine )
1423 {
1424 auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
1425
1426 if( point == bus_entry->GetPosition() )
1427 bus_entry->m_connected_bus_items[0] = busLine;
1428 else
1429 bus_entry->m_connected_bus_items[1] = busLine;
1430
1431 bus_entry->AddConnectionTo( aSheet, busLine );
1432 busLine->AddConnectionTo( aSheet, bus_entry );
1433 continue;
1434 }
1435 }
1436 // Change junctions to be on bus junction layer if they are touching a bus
1437 else if( connected_item->Type() == SCH_JUNCTION_T )
1438 {
1439 connected_item->SetLayer( busLine ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
1440 }
1441
1442 for( SCH_ITEM* test_item : connection_vec )
1443 {
1444 bool bus_connection_ok = true;
1445
1446 if( test_item == connected_item )
1447 continue;
1448
1449 // Set up the link between the bus entry net and the bus
1450 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1451 {
1452 if( test_item->GetLayer() == LAYER_BUS )
1453 {
1454 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1455 bus_entry->m_connected_bus_item = test_item;
1456 }
1457 }
1458
1459 // Bus entries only connect to bus lines on the end that is touching a bus line.
1460 // If the user has overlapped another net line with the endpoint of the bus entry
1461 // where the entry connects to a bus, we don't want to short-circuit it.
1462 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1463 {
1464 bus_connection_ok = !busLine || test_item->GetLayer() == LAYER_BUS;
1465 }
1466 else if( test_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1467 {
1468 bus_connection_ok = !busLine || connected_item->GetLayer() == LAYER_BUS;
1469 }
1470
1471 if( connected_item->ConnectionPropagatesTo( test_item )
1472 && test_item->ConnectionPropagatesTo( connected_item )
1473 && bus_connection_ok )
1474 {
1475 connected_item->AddConnectionTo( aSheet, test_item );
1476 }
1477 }
1478
1479 // If we got this far and did not find a connected bus item for a bus entry,
1480 // we should do a manual scan in case there is a bus item on this connection
1481 // point but we didn't pick it up earlier because there is *also* a net item here.
1482 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1483 {
1484 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1485
1486 if( !bus_entry->m_connected_bus_item )
1487 {
1488 SCH_SCREEN* screen = aSheet.LastScreen();
1489 SCH_LINE* bus = screen->GetBus( point );
1490
1491 if( bus )
1492 bus_entry->m_connected_bus_item = bus;
1493 }
1494 }
1495 }
1496 }
1497}
1498
1499
1501{
1502 // Recache all bus aliases for later use
1503 wxCHECK_RET( m_schematic, wxS( "Connection graph cannot be built without schematic pointer" ) );
1504
1505 m_bus_alias_cache.clear();
1506
1507 for( const std::shared_ptr<BUS_ALIAS>& alias : m_schematic->GetAllBusAliases() )
1508 {
1509 if( alias )
1510 m_bus_alias_cache[alias->GetName()] = alias;
1511 }
1512
1513 // Build subgraphs from items (on a per-sheet basis)
1514 for( SCH_ITEM* item : m_items )
1515 {
1516 for( const auto& [sheet, connection] : item->m_connection_map )
1517 {
1518 if( connection->SubgraphCode() == 0 )
1519 {
1520 CONNECTION_SUBGRAPH* subgraph = new CONNECTION_SUBGRAPH( this );
1521
1522 subgraph->m_code = m_last_subgraph_code++;
1523 subgraph->m_sheet = sheet;
1524
1525 subgraph->AddItem( item );
1526
1527 connection->SetSubgraphCode( subgraph->m_code );
1528 m_item_to_subgraph_map[item] = subgraph;
1529
1530 std::list<SCH_ITEM*> memberlist;
1531
1532 auto get_items =
1533 [&]( SCH_ITEM* aItem ) -> bool
1534 {
1535 SCH_CONNECTION* conn = aItem->GetOrInitConnection( sheet, this );
1536 bool unique = !( aItem->GetFlags() & CONNECTIVITY_CANDIDATE );
1537
1538 if( conn && !conn->SubgraphCode() )
1539 aItem->SetFlags( CONNECTIVITY_CANDIDATE );
1540
1541 return ( unique && conn && ( conn->SubgraphCode() == 0 ) );
1542 };
1543
1544 std::copy_if( item->ConnectedItems( sheet ).begin(),
1545 item->ConnectedItems( sheet ).end(),
1546 std::back_inserter( memberlist ), get_items );
1547
1548 for( SCH_ITEM* connected_item : memberlist )
1549 {
1550 if( connected_item->Type() == SCH_NO_CONNECT_T )
1551 subgraph->m_no_connect = connected_item;
1552
1553 SCH_CONNECTION* connected_conn = connected_item->Connection( &sheet );
1554
1555 wxCHECK2( connected_conn, continue );
1556
1557 if( connected_conn->SubgraphCode() == 0 )
1558 {
1559 connected_conn->SetSubgraphCode( subgraph->m_code );
1560 m_item_to_subgraph_map[connected_item] = subgraph;
1561 subgraph->AddItem( connected_item );
1562 const SCH_ITEM_VEC& citemset = connected_item->ConnectedItems( sheet );
1563
1564 for( SCH_ITEM* citem : citemset )
1565 {
1566 if( citem->HasFlag( CONNECTIVITY_CANDIDATE ) )
1567 continue;
1568
1569 if( get_items( citem ) )
1570 memberlist.push_back( citem );
1571 }
1572 }
1573 }
1574
1575 for( SCH_ITEM* connected_item : memberlist )
1576 connected_item->ClearFlags( CONNECTIVITY_CANDIDATE );
1577
1578 subgraph->m_dirty = true;
1579 m_subgraphs.push_back( subgraph );
1580 }
1581 }
1582 }
1583}
1584
1585
1587{
1588 // Resolve drivers for subgraphs and propagate connectivity info
1589 std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
1590
1591 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
1592 [&] ( const CONNECTION_SUBGRAPH* candidate )
1593 {
1594 return candidate->m_dirty;
1595 } );
1596
1597 wxLogTrace( ConnTrace, wxT( "Resolving drivers for %zu subgraphs" ), dirty_graphs.size() );
1598
1599 std::vector<std::future<size_t>> returns( dirty_graphs.size() );
1600
1601 auto update_lambda =
1602 []( CONNECTION_SUBGRAPH* subgraph ) -> size_t
1603 {
1604 if( !subgraph->m_dirty )
1605 return 0;
1606
1607 // Special processing for some items
1608 for( SCH_ITEM* item : subgraph->m_items )
1609 {
1610 switch( item->Type() )
1611 {
1612 case SCH_NO_CONNECT_T:
1613 subgraph->m_no_connect = item;
1614 break;
1615
1617 subgraph->m_bus_entry = item;
1618 break;
1619
1620 case SCH_PIN_T:
1621 {
1622 auto pin = static_cast<SCH_PIN*>( item );
1623
1624 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
1625 subgraph->m_no_connect = item;
1626
1627 break;
1628 }
1629
1630 default:
1631 break;
1632 }
1633 }
1634
1635 subgraph->ResolveDrivers( true );
1636 subgraph->m_dirty = false;
1637
1638 return 1;
1639 };
1640
1642
1643 auto results = tp.submit_loop( 0, dirty_graphs.size(),
1644 [&]( const int ii )
1645 {
1646 update_lambda( dirty_graphs[ii] );
1647 } );
1648 results.wait();
1649
1650 // Now discard any non-driven subgraphs from further consideration
1651
1652 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
1653 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1654 {
1655 return candidate->m_driver;
1656 } );
1657}
1658
1659
1661{
1662 // Check for subgraphs with the same net name but only weak drivers.
1663 // For example, two wires that are both connected to hierarchical
1664 // sheet pins that happen to have the same name, but are not the same.
1665
1666 for( auto&& subgraph : m_driver_subgraphs )
1667 {
1668 wxString full_name = subgraph->m_driver_connection->Name();
1669 wxString name = subgraph->m_driver_connection->Name( true );
1670 m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
1671
1672 // For vector buses, we need to cache the prefix also, as two different instances of the
1673 // weakly driven pin may have the same prefix but different vector start and end. We need
1674 // to treat those as needing renaming also, because otherwise if they end up on a sheet with
1675 // common usage, they will be incorrectly merged.
1676 if( subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1677 {
1678 wxString prefixOnly = full_name.BeforeFirst( '[' ) + wxT( "[]" );
1679 m_net_name_to_subgraphs_map[prefixOnly].emplace_back( subgraph );
1680 }
1681
1682 subgraph->m_dirty = true;
1683
1684 if( subgraph->m_strong_driver )
1685 {
1686 SCH_ITEM* driver = subgraph->m_driver;
1687 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1688
1689 switch( driver->Type() )
1690 {
1691 case SCH_LABEL_T:
1692 case SCH_HIER_LABEL_T:
1693 {
1694 m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
1695 break;
1696 }
1697 case SCH_GLOBAL_LABEL_T:
1698 {
1699 m_global_label_cache[name].push_back( subgraph );
1700 break;
1701 }
1702 case SCH_PIN_T:
1703 {
1704 SCH_PIN* pin = static_cast<SCH_PIN*>( driver );
1705 if( pin->IsGlobalPower() )
1706 {
1707 m_global_label_cache[name].push_back( subgraph );
1708 }
1709 else if( pin->IsLocalPower() )
1710 {
1711 m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
1712 }
1713 else
1714 {
1715 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
1716 wxLogTrace( ConnTrace, wxS( "Unexpected normal pin %s" ),
1717 driver->GetItemDescription( &unitsProvider, true ) );
1718 }
1719
1720 break;
1721 }
1722 default:
1723 {
1724 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
1725
1726 wxLogTrace( ConnTrace, wxS( "Unexpected strong driver %s" ),
1727 driver->GetItemDescription( &unitsProvider, true ) );
1728 break;
1729 }
1730 }
1731 }
1732 }
1733}
1734
1735
1737{
1738 std::vector<CONNECTION_SUBGRAPH*> new_subgraphs;
1739
1740 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1741 {
1742 SCH_ITEM_VEC vec = subgraph->GetAllBusLabels();
1743
1744 for( SCH_ITEM* item : vec )
1745 {
1746 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
1747
1748 SCH_CONNECTION dummy( item, subgraph->m_sheet );
1749 dummy.SetGraph( this );
1750 dummy.ConfigureFromLabel( label->GetShownText( &subgraph->m_sheet, false ) );
1751
1752 wxLogTrace( ConnTrace, wxS( "new bus label (%s)" ),
1753 label->GetShownText( &subgraph->m_sheet, false ) );
1754
1755 for( const auto& conn : dummy.Members() )
1756 {
1757 // Only create subgraphs for NET members, not nested buses
1758 if( !conn->IsNet() )
1759 continue;
1760
1761 wxString name = conn->FullLocalName();
1762
1763 CONNECTION_SUBGRAPH* new_sg = new CONNECTION_SUBGRAPH( this );
1764
1765 // This connection cannot form a part of the item because the item is not, itself
1766 // connected to this subgraph. It exists as part of a virtual item that may be
1767 // connected to other items but is not in the schematic.
1768 auto new_conn = std::make_unique<SCH_CONNECTION>( item, subgraph->m_sheet );
1769 new_conn->SetGraph( this );
1770 new_conn->SetName( name );
1771 new_conn->SetType( CONNECTION_TYPE::NET );
1772
1773 SCH_CONNECTION* new_conn_ptr = subgraph->StoreImplicitConnection( std::move( new_conn ) );
1774 int code = assignNewNetCode( *new_conn_ptr );
1775
1776 wxLogTrace( ConnTrace, wxS( "SG(%ld), Adding full local name (%s) with sg (%d) on subsheet %s" ),
1777 subgraph->m_code, name, code, subgraph->m_sheet.PathHumanReadable() );
1778
1779 new_sg->m_driver_connection = new_conn_ptr;
1780 new_sg->m_code = m_last_subgraph_code++;
1781 new_sg->m_sheet = subgraph->GetSheet();
1782 new_sg->m_is_bus_member = true;
1783 new_sg->m_strong_driver = true;
1784
1786 NET_NAME_CODE_CACHE_KEY key = { new_sg->GetNetName(), code };
1787 m_net_code_to_subgraphs_map[ key ].push_back( new_sg );
1788 m_net_name_to_subgraphs_map[ name ].push_back( new_sg );
1789 m_subgraphs.push_back( new_sg );
1790 new_subgraphs.push_back( new_sg );
1791 }
1792 }
1793 }
1794
1795 std::copy( new_subgraphs.begin(), new_subgraphs.end(),
1796 std::back_inserter( m_driver_subgraphs ) );
1797}
1798
1799
1801{
1802 // Generate subgraphs for global power pins. These will be merged with other subgraphs
1803 // on the same sheet in the next loop.
1804 // These are NOT limited to power symbols, we support legacy invisible + power-in pins
1805 // on non-power symbols.
1806
1807 // Sort power pins for deterministic processing order. This ensures that when multiple
1808 // power pins share the same net name, the same pin consistently creates the subgraph
1809 // across different ERC runs.
1810 std::sort( m_global_power_pins.begin(), m_global_power_pins.end(),
1811 []( const std::pair<SCH_SHEET_PATH, SCH_PIN*>& a,
1812 const std::pair<SCH_SHEET_PATH, SCH_PIN*>& b )
1813 {
1814 int pathCmp = a.first.Cmp( b.first );
1815
1816 if( pathCmp != 0 )
1817 return pathCmp < 0;
1818
1819 const SCH_SYMBOL* symA = static_cast<const SCH_SYMBOL*>( a.second->GetParentSymbol() );
1820 const SCH_SYMBOL* symB = static_cast<const SCH_SYMBOL*>( b.second->GetParentSymbol() );
1821
1822 wxString refA = symA ? symA->GetRef( &a.first, false ) : wxString();
1823 wxString refB = symB ? symB->GetRef( &b.first, false ) : wxString();
1824
1825 int refCmp = refA.Cmp( refB );
1826
1827 if( refCmp != 0 )
1828 return refCmp < 0;
1829
1830 return a.second->GetNumber().Cmp( b.second->GetNumber() ) < 0;
1831 } );
1832
1833 std::unordered_map<int, CONNECTION_SUBGRAPH*> global_power_pin_subgraphs;
1834
1835 for( const auto& [sheet, pin] : m_global_power_pins )
1836 {
1837 SYMBOL* libParent = pin->GetLibPin() ? pin->GetLibPin()->GetParentSymbol() : nullptr;
1838
1839 if( !pin->ConnectedItems( sheet ).empty()
1840 && ( !libParent || !libParent->IsGlobalPower() ) )
1841 {
1842 // ERC will warn about this: user has wired up an invisible pin
1843 continue;
1844 }
1845
1846 SCH_CONNECTION* connection = pin->GetOrInitConnection( sheet, this );
1847
1848 // If this pin already has a subgraph, don't need to process
1849 if( !connection || connection->SubgraphCode() > 0 )
1850 continue;
1851
1852 // Proper modern power symbols get their net name from the value field
1853 // in the symbol, but we support legacy non-power symbols with global
1854 // power connections based on invisible, power-in, pin's names.
1855 if( libParent && libParent->IsGlobalPower() )
1856 connection->SetName( pin->GetParentSymbol()->GetValue( true, &sheet, false ) );
1857 else
1858 connection->SetName( pin->GetShownName() );
1859
1860 int code = assignNewNetCode( *connection );
1861
1862 connection->SetNetCode( code );
1863
1864 CONNECTION_SUBGRAPH* subgraph;
1865 auto jj = global_power_pin_subgraphs.find( code );
1866
1867 if( jj != global_power_pin_subgraphs.end() )
1868 {
1869 subgraph = jj->second;
1870 subgraph->AddItem( pin );
1871 }
1872 else
1873 {
1874 subgraph = new CONNECTION_SUBGRAPH( this );
1875
1876 subgraph->m_code = m_last_subgraph_code++;
1877 subgraph->m_sheet = sheet;
1878
1879 subgraph->AddItem( pin );
1880 subgraph->ResolveDrivers();
1881
1882 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(), code };
1883 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1884 m_subgraphs.push_back( subgraph );
1885 m_driver_subgraphs.push_back( subgraph );
1886
1887 global_power_pin_subgraphs[code] = subgraph;
1888 }
1889
1890 connection->SetSubgraphCode( subgraph->m_code );
1891 }
1892}
1893
1894
1896{
1897 // Here we do all the local (sheet) processing of each subgraph, including assigning net
1898 // codes, merging subgraphs together that use label connections, etc.
1899
1900 // Cache remaining valid subgraphs by sheet path
1901 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1902 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1903
1904 std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
1905
1906 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1907 {
1908 if( subgraph->m_absorbed )
1909 continue;
1910
1911 SCH_CONNECTION* connection = subgraph->m_driver_connection;
1912 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1913 wxString name = connection->Name();
1914
1915 // Test subgraphs with weak drivers for net name conflicts and fix them
1916 unsigned suffix = 1;
1917
1918 auto create_new_name =
1919 [&suffix]( SCH_CONNECTION* aConn ) -> wxString
1920 {
1921 wxString newName;
1922 wxString suffixStr = std::to_wstring( suffix );
1923
1924 // For group buses with a prefix, we can add the suffix to the prefix.
1925 // If they don't have a prefix, we force the creation of a prefix so that
1926 // two buses don't get inadvertently shorted together.
1927 if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
1928 {
1929 wxString prefix = aConn->BusPrefix();
1930
1931 if( prefix.empty() )
1932 prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
1933
1934 wxString oldName = aConn->Name().AfterFirst( '{' );
1935
1936 newName << prefix << wxT( "_" ) << suffixStr << wxT( "{" ) << oldName;
1937
1938 aConn->ConfigureFromLabel( newName );
1939 }
1940 else
1941 {
1942 newName << aConn->Name() << wxT( "_" ) << suffixStr;
1943 aConn->SetSuffix( wxString( wxT( "_" ) ) << suffixStr );
1944 }
1945
1946 suffix++;
1947 return newName;
1948 };
1949
1950 if( !subgraph->m_strong_driver )
1951 {
1952 std::vector<CONNECTION_SUBGRAPH*> vec_empty;
1953 std::vector<CONNECTION_SUBGRAPH*>* vec = &vec_empty;
1954
1955 if( m_net_name_to_subgraphs_map.count( name ) )
1956 vec = &m_net_name_to_subgraphs_map.at( name );
1957
1958 // If we are a unique bus vector, check if we aren't actually unique because of another
1959 // subgraph with a similar bus vector
1960 if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1961 {
1962 wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
1963
1964 if( m_net_name_to_subgraphs_map.count( prefixOnly ) )
1965 vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
1966 }
1967
1968 if( vec->size() > 1 )
1969 {
1970 wxString new_name = create_new_name( connection );
1971
1972 while( m_net_name_to_subgraphs_map.contains( new_name ) )
1973 new_name = create_new_name( connection );
1974
1975 wxLogTrace( ConnTrace, wxS( "%ld (%s) is weakly driven and not unique. Changing to %s." ),
1976 subgraph->m_code, name, new_name );
1977
1978 std::erase( *vec, subgraph );
1979
1980 m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1981
1982 name = new_name;
1983 }
1984 else if( subgraph->m_driver )
1985 {
1986 // If there is no conflict, promote sheet pins to be strong drivers so that they
1987 // will be considered below for propagation/merging.
1988
1989 // It is possible for this to generate a conflict if the sheet pin has the same
1990 // name as a global label on the same sheet, because global merging will then treat
1991 // this subgraph as if it had a matching local label. So, for those cases, we
1992 // don't apply this promotion
1993
1994 if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1995 {
1996 bool conflict = false;
1997 wxString global_name = connection->Name( true );
1998 auto kk = m_net_name_to_subgraphs_map.find( global_name );
1999
2000 if( kk != m_net_name_to_subgraphs_map.end() )
2001 {
2002 // A global will conflict if it is on the same sheet as this subgraph, since
2003 // it would be connected by implicit local label linking
2004 std::vector<CONNECTION_SUBGRAPH*>& candidates = kk->second;
2005
2006 for( const CONNECTION_SUBGRAPH* candidate : candidates )
2007 {
2008 if( candidate->m_sheet == sheet )
2009 conflict = true;
2010 }
2011 }
2012
2013 if( conflict )
2014 {
2015 wxLogTrace( ConnTrace, wxS( "%ld (%s) skipped for promotion due to potential conflict" ),
2016 subgraph->m_code, name );
2017 }
2018 else
2019 {
2020 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
2021
2022 wxLogTrace( ConnTrace, wxS( "%ld (%s) weakly driven by unique sheet pin %s, promoting" ),
2023 subgraph->m_code, name,
2024 subgraph->m_driver->GetItemDescription( &unitsProvider, true ) );
2025
2026 subgraph->m_strong_driver = true;
2027 }
2028 }
2029 }
2030 }
2031
2032 // Assign net codes
2033 if( connection->IsBus() )
2034 {
2035 int code = -1;
2036 auto it = m_bus_name_to_code_map.find( name );
2037
2038 if( it != m_bus_name_to_code_map.end() )
2039 {
2040 code = it->second;
2041 }
2042 else
2043 {
2044 code = m_last_bus_code++;
2045 m_bus_name_to_code_map[ name ] = code;
2046 }
2047
2048 connection->SetBusCode( code );
2049 assignNetCodesToBus( connection );
2050 }
2051 else
2052 {
2053 assignNewNetCode( *connection );
2054 }
2055
2056 // Reset the flag for the next loop below
2057 subgraph->m_dirty = true;
2058
2059 // Next, we merge together subgraphs that have label connections, and create
2060 // neighbor links for subgraphs that are part of a bus on the same sheet.
2061 // For merging, we consider each possible strong driver.
2062
2063 // If this subgraph doesn't have a strong driver, let's skip it, since there is no
2064 // way it will be merged with anything.
2065 if( !subgraph->m_strong_driver )
2066 continue;
2067
2068 // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
2069 // as the subgraph we are considering that has a strong driver.
2070 // Weakly driven subgraphs are not considered since they will never be absorbed or
2071 // form neighbor links.
2072 std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
2073 std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
2074 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
2075 std::back_inserter( candidate_subgraphs ),
2076 [&] ( const CONNECTION_SUBGRAPH* candidate )
2077 {
2078 return ( !candidate->m_absorbed &&
2079 candidate->m_strong_driver &&
2080 candidate != subgraph );
2081 } );
2082
2083 // This is a list of connections on the current subgraph to compare to the
2084 // drivers of each candidate subgraph. If the current subgraph is a bus,
2085 // we should consider each bus member.
2086 std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
2087
2088 // Also check the main driving connection
2089 connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
2090
2091 auto add_connections_to_check =
2092 [&] ( CONNECTION_SUBGRAPH* aSubgraph )
2093 {
2094 for( SCH_ITEM* possible_driver : aSubgraph->m_items )
2095 {
2096 if( possible_driver == aSubgraph->m_driver )
2097 continue;
2098
2099 auto c = getDefaultConnection( possible_driver, aSubgraph );
2100
2101 if( c )
2102 {
2103 if( c->Type() != aSubgraph->m_driver_connection->Type() )
2104 continue;
2105
2106 if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
2107 continue;
2108
2109 connections_to_check.push_back( c );
2110 wxLogTrace( ConnTrace, wxS( "%lu (%s): Adding secondary driver %s" ),
2111 aSubgraph->m_code,
2112 aSubgraph->m_driver_connection->Name( true ),
2113 c->Name( true ) );
2114 }
2115 }
2116 };
2117
2118 // Now add other strong drivers
2119 // The actual connection attached to these items will have been overwritten
2120 // by the chosen driver of the subgraph, so we need to create a dummy connection
2121 add_connections_to_check( subgraph );
2122
2123 std::set<SCH_CONNECTION*> checked_connections;
2124
2125 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2126 {
2127 auto member = connections_to_check[i];
2128
2129 // Don't check the same connection twice
2130 if( !checked_connections.insert( member.get() ).second )
2131 continue;
2132
2133 if( member->IsBus() )
2134 {
2135 connections_to_check.insert( connections_to_check.end(),
2136 member->Members().begin(),
2137 member->Members().end() );
2138 }
2139
2140 wxString test_name = member->Name( true );
2141
2142 for( CONNECTION_SUBGRAPH* candidate : candidate_subgraphs )
2143 {
2144 if( candidate->m_absorbed || candidate == subgraph )
2145 continue;
2146
2147 bool match = false;
2148
2149 if( candidate->m_driver_connection->Name( true ) == test_name )
2150 {
2151 match = true;
2152 }
2153 else
2154 {
2155 if( !candidate->m_multiple_drivers )
2156 continue;
2157
2158 for( SCH_ITEM *driver : candidate->m_drivers )
2159 {
2160 if( driver == candidate->m_driver )
2161 continue;
2162
2163 // Sheet pins are not candidates for merging
2164 if( driver->Type() == SCH_SHEET_PIN_T )
2165 continue;
2166
2167 if( driver->Type() == SCH_PIN_T )
2168 {
2169 auto pin = static_cast<SCH_PIN*>( driver );
2170
2171 if( pin->IsPower()
2172 && pin->GetDefaultNetName( sheet ) == test_name )
2173 {
2174 match = true;
2175 break;
2176 }
2177 }
2178 else
2179 {
2180 // Should we skip this if the driver type is not one of these types?
2181 wxASSERT( driver->Type() == SCH_LABEL_T ||
2182 driver->Type() == SCH_GLOBAL_LABEL_T ||
2183 driver->Type() == SCH_HIER_LABEL_T );
2184
2185 if( subgraph->GetNameForDriver( driver ) == test_name )
2186 {
2187 match = true;
2188 break;
2189 }
2190 }
2191 }
2192 }
2193
2194 if( match )
2195 {
2196 if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
2197 {
2198 wxLogTrace( ConnTrace, wxS( "%lu (%s) has bus child %lu (%s)" ),
2199 subgraph->m_code, connection->Name(),
2200 candidate->m_code, member->Name() );
2201
2202 subgraph->m_bus_neighbors[member].insert( candidate );
2203 candidate->m_bus_parents[member].insert( subgraph );
2204 }
2205 else if( ( !connection->IsBus()
2206 && !candidate->m_driver_connection->IsBus() )
2207 || connection->Type() == candidate->m_driver_connection->Type() )
2208 {
2209 wxLogTrace( ConnTrace, wxS( "%lu (%s) absorbs neighbor %lu (%s)" ),
2210 subgraph->m_code, connection->Name(),
2211 candidate->m_code, candidate->m_driver_connection->Name() );
2212
2213 // Candidate may have other non-chosen drivers we need to follow
2214 add_connections_to_check( candidate );
2215
2216 subgraph->Absorb( candidate );
2217 invalidated_subgraphs.insert( subgraph );
2218 }
2219 }
2220 }
2221 }
2222 }
2223
2224 // Update any subgraph that was invalidated above
2225 for( CONNECTION_SUBGRAPH* subgraph : invalidated_subgraphs )
2226 {
2227 if( subgraph->m_absorbed )
2228 continue;
2229
2230 if( !subgraph->ResolveDrivers() )
2231 continue;
2232
2233 if( subgraph->m_driver_connection->IsBus() )
2234 assignNetCodesToBus( subgraph->m_driver_connection );
2235 else
2236 assignNewNetCode( *subgraph->m_driver_connection );
2237
2238 wxLogTrace( ConnTrace, wxS( "Re-resolving drivers for %lu (%s)" ),
2239 subgraph->m_code, subgraph->m_driver_connection->Name() );
2240 }
2241
2242}
2243
2244
2245// TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
2246// to the same subgraph necessarily if it runs over and over again on the same
2247// sheet. We need:
2248//
2249// a) a cache of net/bus codes, like used before
2250// b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
2251// c) some way of trying to avoid changing net names. so we should keep track
2252// of the previous driver of a net, and if it comes down to choosing between
2253// equally-prioritized drivers, choose the one that already exists as a driver
2254// on some portion of the items.
2255
2256
2257void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2258 bool aUnconditional )
2259{
2260 // Recache all bus aliases for later use
2261 wxCHECK_RET( m_schematic, wxT( "Connection graph cannot be built without schematic pointer" ) );
2262
2263 m_bus_alias_cache.clear();
2264
2265 for( const std::shared_ptr<BUS_ALIAS>& alias : m_schematic->GetAllBusAliases() )
2266 {
2267 if( alias )
2268 m_bus_alias_cache[alias->GetName()] = alias;
2269 }
2270
2271 PROF_TIMER sub_graph( "buildItemSubGraphs" );
2273
2274 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
2275 sub_graph.Show();
2276
2281
2283
2285
2287
2289
2290 PROF_TIMER proc_sub_graph( "ProcessSubGraphs" );
2292
2293 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
2294 proc_sub_graph.Show();
2295
2296 // Absorbed subgraphs should no longer be considered
2297 std::erase_if( m_driver_subgraphs, [&]( const CONNECTION_SUBGRAPH* candidate ) -> bool
2298 {
2299 return candidate->m_absorbed;
2300 } );
2301
2302 // Store global subgraphs for later reference
2303 std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
2304 std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
2305 std::back_inserter( global_subgraphs ),
2306 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
2307 {
2308 return !candidate->m_local_driver;
2309 } );
2310
2311 // Recache remaining valid subgraphs by sheet path
2313
2314 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2315 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
2316
2318
2319 auto results = tp.submit_loop( 0, m_driver_subgraphs.size(),
2320 [&]( const int ii )
2321 {
2322 m_driver_subgraphs[ii]->UpdateItemConnections();
2323 });
2324
2325 results.wait();
2326
2327 // Next time through the subgraphs, we do some post-processing to handle things like
2328 // connecting bus members to their neighboring subgraphs, and then propagate connections
2329 // through the hierarchy
2330 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2331 {
2332 if( !subgraph->m_dirty )
2333 continue;
2334
2335 wxLogTrace( ConnTrace, wxS( "Processing %lu (%s) for propagation" ),
2336 subgraph->m_code, subgraph->m_driver_connection->Name() );
2337
2338 // For subgraphs that are driven by a global (power port or label) and have more
2339 // than one global driver, we need to seek out other subgraphs driven by the
2340 // same name as the non-chosen driver and update them to match the chosen one.
2341
2342 if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
2343 {
2344 for( SCH_ITEM* driver : subgraph->m_drivers )
2345 {
2346 if( driver == subgraph->m_driver )
2347 continue;
2348
2349 const wxString& secondary_name = subgraph->GetNameForDriver( driver );
2350
2351 if( secondary_name == subgraph->m_driver_connection->Name() )
2352 continue;
2353
2354 bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
2356
2357 for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
2358 {
2359 if( candidate == subgraph )
2360 continue;
2361
2362 if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
2363 continue;
2364
2365 for( SCH_ITEM* candidate_driver : candidate->m_drivers )
2366 {
2367 if( candidate->GetNameForDriver( candidate_driver ) == secondary_name )
2368 {
2369 wxLogTrace( ConnTrace, wxS( "Global %lu (%s) promoted to %s" ),
2370 candidate->m_code, candidate->m_driver_connection->Name(),
2371 subgraph->m_driver_connection->Name() );
2372
2373 candidate->m_driver_connection->Clone( *subgraph->m_driver_connection );
2374
2375 candidate->m_dirty = false;
2376 propagateToNeighbors( candidate, false );
2377 }
2378 }
2379 }
2380 }
2381 }
2382
2383 // This call will handle descending the hierarchy and updating child subgraphs
2384 propagateToNeighbors( subgraph, false );
2385 }
2386
2387 // After processing and allowing some to be skipped if they have hierarchical
2388 // pins connecting both up and down the hierarchy, we check to see if any of them
2389 // have not been processed. This would indicate that they do not have off-sheet connections
2390 // but we still need to handle the subgraph
2391 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2392 {
2393 if( subgraph->m_dirty )
2394 propagateToNeighbors( subgraph, true );
2395 }
2396
2397 // Handle buses that have been linked together somewhere by member (net) connections.
2398 // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
2399
2400 // For net subgraphs that have more than one bus parent, we need to ensure that those
2401 // buses are linked together in the final netlist. The final name of each bus might not
2402 // match the local name that was used to establish the parent-child relationship, because
2403 // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
2404 // we need to identify the appropriate bus members to link together (and their final names),
2405 // and then update all instances of the old name in the hierarchy.
2406 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2407 {
2408 // All SGs should have been processed by propagateToNeighbors above
2409 // Should we skip all of this if the subgraph is not dirty?
2410 wxASSERT_MSG( !subgraph->m_dirty,
2411 wxS( "Subgraph not processed by propagateToNeighbors!" ) );
2412
2413 if( subgraph->m_bus_parents.size() < 2 )
2414 continue;
2415
2416 SCH_CONNECTION* conn = subgraph->m_driver_connection;
2417
2418 wxLogTrace( ConnTrace, wxS( "%lu (%s) has multiple bus parents" ),
2419 subgraph->m_code, conn->Name() );
2420
2421 // Should we skip everything after this if this is not a net?
2422 wxCHECK2( conn->IsNet(), continue );
2423
2424 for( const auto& ii : subgraph->m_bus_parents )
2425 {
2426 SCH_CONNECTION* link_member = ii.first.get();
2427
2428 for( CONNECTION_SUBGRAPH* parent : ii.second )
2429 {
2430 while( parent->m_absorbed )
2431 parent = parent->m_absorbed_by;
2432
2433 SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
2434
2435 if( !match )
2436 {
2437 wxLogTrace( ConnTrace, wxS( "Warning: could not match %s inside %lu (%s)" ),
2438 conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
2439 continue;
2440 }
2441
2442 if( conn->Name() != match->Name() )
2443 {
2444 wxString old_name = match->Name();
2445
2446 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ),
2447 parent->m_code, parent->m_driver_connection->Name(), old_name, conn->Name() );
2448
2449 match->Clone( *conn );
2450
2451 auto jj = m_net_name_to_subgraphs_map.find( old_name );
2452
2453 if( jj == m_net_name_to_subgraphs_map.end() )
2454 continue;
2455
2456 // Copy the vector to avoid iterator invalidation when recaching
2457 std::vector<CONNECTION_SUBGRAPH*> old_subgraphs = jj->second;
2458
2459 for( CONNECTION_SUBGRAPH* old_sg : old_subgraphs )
2460 {
2461 while( old_sg->m_absorbed )
2462 old_sg = old_sg->m_absorbed_by;
2463
2464 wxString old_sg_name = old_sg->m_driver_connection->Name();
2465 old_sg->m_driver_connection->Clone( *conn );
2466
2467 if( old_sg_name != old_sg->m_driver_connection->Name() )
2468 recacheSubgraphName( old_sg, old_sg_name );
2469 }
2470 }
2471 }
2472 }
2473 }
2474
2475 auto updateItemConnectionsTask =
2476 [&]( CONNECTION_SUBGRAPH* subgraph ) -> size_t
2477 {
2478 // Make sure weakly-driven single-pin nets get the unconnected_ prefix
2479 if( !subgraph->m_strong_driver
2480 && subgraph->m_drivers.size() == 1
2481 && subgraph->m_driver->Type() == SCH_PIN_T )
2482 {
2483 SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
2484 wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
2485
2486 subgraph->m_driver_connection->ConfigureFromLabel( name );
2487 }
2488
2489 subgraph->m_dirty = false;
2490 subgraph->UpdateItemConnections();
2491
2492 // No other processing to do on buses
2493 if( subgraph->m_driver_connection->IsBus() )
2494 return 0;
2495
2496 // As a visual aid, we can check sheet pins that are driven by themselves to see
2497 // if they should be promoted to buses
2498 if( subgraph->m_driver && subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
2499 {
2500 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
2501
2502 if( SCH_SHEET* sheet = pin->GetParent() )
2503 {
2504 wxString pinText = pin->GetShownText( false );
2505 SCH_SCREEN* screen = sheet->GetScreen();
2506
2507 for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
2508 {
2509 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
2510
2511 if( label->GetShownText( &subgraph->m_sheet, false ) == pinText )
2512 {
2513 SCH_SHEET_PATH path = subgraph->m_sheet;
2514 path.push_back( sheet );
2515
2516 SCH_CONNECTION* parent_conn = label->Connection( &path );
2517
2518 if( parent_conn && parent_conn->IsBus() )
2519 subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
2520
2521 break;
2522 }
2523 }
2524
2525 if( subgraph->m_driver_connection->IsBus() )
2526 return 0;
2527 }
2528 }
2529
2530 return 1;
2531 };
2532
2533 auto results2 = tp.submit_loop( 0, m_driver_subgraphs.size(),
2534 [&]( const int ii )
2535 {
2536 updateItemConnectionsTask( m_driver_subgraphs[ii] );
2537 } );
2538 results2.wait();
2539
2542
2543 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2544 {
2545 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(),
2546 subgraph->m_driver_connection->NetCode() };
2547 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
2548
2549 m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
2550 }
2551
2552 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Project().GetProjectFile().m_NetSettings;
2553 std::map<wxString, std::set<wxString>> oldAssignments = netSettings->GetNetclassLabelAssignments();
2554 std::set<wxString> affectedNetclassNetAssignments;
2555
2556 netSettings->ClearNetclassLabelAssignments();
2557
2558 auto dirtySubgraphs =
2559 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2560 {
2561 if( aChangedItemHandler )
2562 {
2563 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2564 {
2565 for( SCH_ITEM* item : subgraph->m_items )
2566 (*aChangedItemHandler)( item );
2567 }
2568 }
2569 };
2570
2571 auto checkNetclassDrivers =
2572 [&]( const wxString& netName, const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2573 {
2574 wxCHECK_RET( !subgraphs.empty(), wxS( "Invalid empty subgraph" ) );
2575
2576 std::set<wxString> netclasses;
2577
2578 // Collect all netclasses on all subgraphs for this net
2579 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2580 {
2581 for( SCH_ITEM* item : subgraph->m_items )
2582 {
2583 for( const auto& [name, provider] : subgraph->GetNetclassesForDriver( item ) )
2584 netclasses.insert( name );
2585 }
2586 }
2587
2588 // Append the netclasses to any included bus members
2589 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2590 {
2591 if( subgraph->m_driver_connection->IsBus() )
2592 {
2593 auto processBusMember = [&, this]( const SCH_CONNECTION* member )
2594 {
2595 if( !netclasses.empty() )
2596 {
2597 netSettings->AppendNetclassLabelAssignment( member->Name(), netclasses );
2598 }
2599
2600 auto ii = m_net_name_to_subgraphs_map.find( member->Name() );
2601
2602 if( oldAssignments.count( member->Name() ) )
2603 {
2604 if( oldAssignments[member->Name()] != netclasses )
2605 {
2606 affectedNetclassNetAssignments.insert( member->Name() );
2607
2608 if( ii != m_net_name_to_subgraphs_map.end() )
2609 dirtySubgraphs( ii->second );
2610 }
2611 }
2612 else if( !netclasses.empty() )
2613 {
2614 affectedNetclassNetAssignments.insert( member->Name() );
2615
2616 if( ii != m_net_name_to_subgraphs_map.end() )
2617 dirtySubgraphs( ii->second );
2618 }
2619 };
2620
2621 for( const std::shared_ptr<SCH_CONNECTION>& member : subgraph->m_driver_connection->Members() )
2622 {
2623 // Check if this member itself is a bus (which can be the case for vector buses as members
2624 // of a bus, see https://gitlab.com/kicad/code/kicad/-/issues/16545
2625 if( member->IsBus() )
2626 {
2627 for( const std::shared_ptr<SCH_CONNECTION>& nestedMember : member->Members() )
2628 processBusMember( nestedMember.get() );
2629 }
2630 else
2631 {
2632 processBusMember( member.get() );
2633 }
2634 }
2635 }
2636 }
2637
2638 // Assign the netclasses to the root netname
2639 if( !netclasses.empty() )
2640 {
2641 netSettings->AppendNetclassLabelAssignment( netName, netclasses );
2642 }
2643
2644 if( oldAssignments.count( netName ) )
2645 {
2646 if( oldAssignments[netName] != netclasses )
2647 {
2648 affectedNetclassNetAssignments.insert( netName );
2649 dirtySubgraphs( subgraphs );
2650 }
2651 }
2652 else if( !netclasses.empty() )
2653 {
2654 affectedNetclassNetAssignments.insert( netName );
2655 dirtySubgraphs( subgraphs );
2656 }
2657 };
2658
2659 // Check for netclass assignments
2660 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
2661 checkNetclassDrivers( netname, subgraphs );
2662
2663 if( !aUnconditional )
2664 {
2665 for( auto& [netname, netclasses] : oldAssignments )
2666 {
2667 if( netSettings->GetNetclassLabelAssignments().count( netname )
2668 || affectedNetclassNetAssignments.count( netname ) )
2669 {
2670 continue;
2671 }
2672
2673 netSettings->SetNetclassLabelAssignment( netname, netclasses );
2674 }
2675 }
2676}
2677
2678
2679int CONNECTION_GRAPH::getOrCreateNetCode( const wxString& aNetName )
2680{
2681 int code;
2682
2683 auto it = m_net_name_to_code_map.find( aNetName );
2684
2685 if( it == m_net_name_to_code_map.end() )
2686 {
2687 code = m_last_net_code++;
2688 m_net_name_to_code_map[ aNetName ] = code;
2689 }
2690 else
2691 {
2692 code = it->second;
2693 }
2694
2695 return code;
2696}
2697
2698
2700{
2701 int code = getOrCreateNetCode( aConnection.Name() );
2702
2703 aConnection.SetNetCode( code );
2704
2705 return code;
2706}
2707
2708
2710{
2711 std::vector<std::shared_ptr<SCH_CONNECTION>> connections_to_check( aConnection->Members() );
2712
2713 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2714 {
2715 const std::shared_ptr<SCH_CONNECTION>& member = connections_to_check[i];
2716
2717 if( member->IsBus() )
2718 {
2719 connections_to_check.insert( connections_to_check.end(),
2720 member->Members().begin(),
2721 member->Members().end() );
2722 continue;
2723 }
2724
2725 assignNewNetCode( *member );
2726 }
2727}
2728
2729
2731{
2732 SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
2733 std::vector<CONNECTION_SUBGRAPH*> search_list;
2734 std::unordered_set<CONNECTION_SUBGRAPH*> visited;
2735 std::unordered_set<SCH_CONNECTION*> stale_bus_members;
2736
2737 auto visit =[&]( CONNECTION_SUBGRAPH* aParent )
2738 {
2739 for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
2740 {
2741 SCH_SHEET_PATH path = aParent->m_sheet;
2742 path.push_back( pin->GetParent() );
2743
2744 auto it = m_sheet_to_subgraphs_map.find( path );
2745
2746 if( it == m_sheet_to_subgraphs_map.end() )
2747 continue;
2748
2749 for( CONNECTION_SUBGRAPH* candidate : it->second )
2750 {
2751 if( !candidate->m_strong_driver
2752 || candidate->m_hier_ports.empty()
2753 || visited.contains( candidate ) )
2754 {
2755 continue;
2756 }
2757
2758 for( SCH_HIERLABEL* label : candidate->m_hier_ports )
2759 {
2760 if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
2761 {
2762 wxLogTrace( ConnTrace, wxS( "%lu: found child %lu (%s)" ), aParent->m_code,
2763 candidate->m_code, candidate->m_driver_connection->Name() );
2764
2765 candidate->m_hier_parent = aParent;
2766 aParent->m_hier_children.insert( candidate );
2767
2768 // Should we skip adding the candidate to the list if the parent and candidate subgraphs
2769 // are not the same?
2770 wxASSERT( candidate->m_graph == aParent->m_graph );
2771
2772 search_list.push_back( candidate );
2773 break;
2774 }
2775 }
2776 }
2777 }
2778
2779 for( SCH_HIERLABEL* label : aParent->m_hier_ports )
2780 {
2781 SCH_SHEET_PATH path = aParent->m_sheet;
2782 path.pop_back();
2783
2784 auto it = m_sheet_to_subgraphs_map.find( path );
2785
2786 if( it == m_sheet_to_subgraphs_map.end() )
2787 continue;
2788
2789 for( CONNECTION_SUBGRAPH* candidate : it->second )
2790 {
2791 if( candidate->m_hier_pins.empty()
2792 || visited.contains( candidate )
2793 || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
2794 {
2795 continue;
2796 }
2797
2798 const KIID& last_parent_uuid = aParent->m_sheet.Last()->m_Uuid;
2799
2800 for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
2801 {
2802 // If the last sheet UUIDs won't match, no need to check the full path
2803 if( pin->GetParent()->m_Uuid != last_parent_uuid )
2804 continue;
2805
2806 SCH_SHEET_PATH pin_path = path;
2807 pin_path.push_back( pin->GetParent() );
2808
2809 if( pin_path != aParent->m_sheet )
2810 continue;
2811
2812 if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
2813 {
2814 wxLogTrace( ConnTrace, wxS( "%lu: found additional parent %lu (%s)" ),
2815 aParent->m_code, candidate->m_code, candidate->m_driver_connection->Name() );
2816
2817 aParent->m_hier_children.insert( candidate );
2818 search_list.push_back( candidate );
2819 break;
2820 }
2821 }
2822 }
2823 }
2824 };
2825
2826 auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph )
2827 {
2828 // Sort bus neighbors by name to ensure deterministic processing order.
2829 // When multiple bus members (e.g., A0, A1, A2, A3) all connect to the same
2830 // shorted net in a child sheet, the first one processed "wins" and sets
2831 // the net name. Sorting ensures the alphabetically-first name is chosen.
2832 std::vector<std::shared_ptr<SCH_CONNECTION>> sortedMembers;
2833
2834 for( const auto& kv : aParentGraph->m_bus_neighbors )
2835 sortedMembers.push_back( kv.first );
2836
2837 std::sort( sortedMembers.begin(), sortedMembers.end(),
2838 []( const std::shared_ptr<SCH_CONNECTION>& a,
2839 const std::shared_ptr<SCH_CONNECTION>& b )
2840 {
2841 return a->Name() < b->Name();
2842 } );
2843
2844 for( const std::shared_ptr<SCH_CONNECTION>& member_conn : sortedMembers )
2845 {
2846 const auto& kv_it = aParentGraph->m_bus_neighbors.find( member_conn );
2847
2848 if( kv_it == aParentGraph->m_bus_neighbors.end() )
2849 continue;
2850
2851 for( CONNECTION_SUBGRAPH* neighbor : kv_it->second )
2852 {
2853 // May have been absorbed but won't have been deleted
2854 while( neighbor->m_absorbed )
2855 neighbor = neighbor->m_absorbed_by;
2856
2857 SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
2858
2859 // Now member may be out of date, since we just cloned the
2860 // connection from higher up in the hierarchy. We need to
2861 // figure out what the actual new connection is.
2862 SCH_CONNECTION* member = matchBusMember( parent, member_conn.get() );
2863
2864 if( !member )
2865 {
2866 // Try harder: we might match on a secondary driver
2867 for( CONNECTION_SUBGRAPH* sg : kv_it->second )
2868 {
2869 if( sg->m_multiple_drivers )
2870 {
2871 SCH_SHEET_PATH sheet = sg->m_sheet;
2872
2873 for( SCH_ITEM* driver : sg->m_drivers )
2874 {
2875 auto c = getDefaultConnection( driver, sg );
2876 member = matchBusMember( parent, c.get() );
2877
2878 if( member )
2879 break;
2880 }
2881 }
2882
2883 if( member )
2884 break;
2885 }
2886 }
2887
2888 // This is bad, probably an ERC error
2889 if( !member )
2890 {
2891 wxLogTrace( ConnTrace, wxS( "Could not match bus member %s in %s" ),
2892 member_conn->Name(), parent->Name() );
2893 continue;
2894 }
2895
2896 SCH_CONNECTION* neighbor_conn = neighbor->m_driver_connection;
2897
2898 wxCHECK2( neighbor_conn, continue );
2899
2900 wxString neighbor_name = neighbor_conn->Name();
2901
2902 // Matching name: no update needed
2903 if( neighbor_name == member->Name() )
2904 continue;
2905
2906 // Was this neighbor already updated from a different sheet? Don't rename it again,
2907 // unless this same parent bus updated it and the bus member name has since changed
2908 // (which can happen when a bus member is renamed via stale member update, issue #18299).
2909 if( neighbor_conn->Sheet() != neighbor->m_sheet )
2910 {
2911 // If the neighbor's connection sheet doesn't match this parent bus's sheet,
2912 // it was updated by a different bus entirely. Don't override.
2913 if( neighbor_conn->Sheet() != parent->Sheet() )
2914 continue;
2915
2916 // If the neighbor's connection sheet matches this parent bus's sheet but
2917 // the names differ, check if the neighbor's current name still matches
2918 // a member of this bus. If it does, the neighbor was updated by a different
2919 // member of this same bus and we should preserve that (determinism).
2920 // If it doesn't match any member, the bus member was renamed and we should update.
2921 SCH_CONNECTION temp( nullptr, neighbor->m_sheet );
2922 temp.ConfigureFromLabel( neighbor_name );
2923
2924 if( matchBusMember( parent, &temp ) )
2925 continue;
2926 }
2927
2928 // Safety check against infinite recursion
2929 wxCHECK2_MSG( neighbor_conn->IsNet(), continue,
2930 wxS( "\"" ) + neighbor_name + wxS( "\" is not a net." ) );
2931
2932 wxLogTrace( ConnTrace, wxS( "%lu (%s) connected to bus member %s (local %s)" ),
2933 neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
2934
2935 // Take whichever name is higher priority
2938 {
2939 member->Clone( *neighbor_conn );
2940 stale_bus_members.insert( member );
2941 }
2942 else
2943 {
2944 neighbor_conn->Clone( *member );
2945
2946 recacheSubgraphName( neighbor, neighbor_name );
2947
2948 // Recurse onto this neighbor in case it needs to re-propagate
2949 neighbor->m_dirty = true;
2950 propagateToNeighbors( neighbor, aForce );
2951
2952 // After hierarchy propagation, the neighbor's connection may have been
2953 // updated to a higher-priority driver (e.g., a power symbol discovered
2954 // through hierarchical sheet pins). If so, update the bus member to match.
2955 // This ensures that net names propagate correctly through bus connections
2956 // that span hierarchical boundaries (issue #18119).
2957 if( neighbor_conn->Name() != member->Name() )
2958 {
2959 member->Clone( *neighbor_conn );
2960 stale_bus_members.insert( member );
2961 }
2962 }
2963 }
2964 }
2965 };
2966
2967 // If we are a bus, we must propagate to local neighbors and then the hierarchy
2968 if( conn->IsBus() )
2969 propagate_bus_neighbors( aSubgraph );
2970
2971 // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
2972 // If we only have one or the other, process (we can either go bottom-up or top-down depending
2973 // on which subgraph comes up first)
2974 if( !aForce && !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
2975 {
2976 wxLogTrace( ConnTrace, wxS( "%lu (%s) has both hier ports and pins; deferring processing" ),
2977 aSubgraph->m_code, conn->Name() );
2978 return;
2979 }
2980 else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
2981 {
2982 wxLogTrace( ConnTrace, wxS( "%lu (%s) has no hier pins or ports on sheet %s; marking clean" ),
2983 aSubgraph->m_code, conn->Name(), aSubgraph->m_sheet.PathHumanReadable() );
2984 aSubgraph->m_dirty = false;
2985 return;
2986 }
2987
2988 visited.insert( aSubgraph );
2989
2990 wxLogTrace( ConnTrace, wxS( "Propagating %lu (%s) to subsheets" ),
2991 aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
2992
2993 visit( aSubgraph );
2994
2995 for( unsigned i = 0; i < search_list.size(); i++ )
2996 {
2997 auto child = search_list[i];
2998
2999 if( visited.insert( child ).second )
3000 visit( child );
3001
3002 child->m_dirty = false;
3003 }
3004
3005 // Now, find the best driver for this chain of subgraphs
3006 CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
3008 bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
3009 wxString bestName = aSubgraph->m_driver_connection->Name();
3010
3011 // Check if a subsheet has a higher-priority connection to the same net
3013 {
3014 for( CONNECTION_SUBGRAPH* subgraph : visited )
3015 {
3016 if( subgraph == aSubgraph )
3017 continue;
3018
3020 CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
3021
3022 bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
3023 wxString candidateName = subgraph->m_driver_connection->Name();
3024 bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
3025 bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
3026
3027 // Pick a better driving subgraph if it:
3028 // a) has a power pin or global driver
3029 // b) is a strong driver and we're a weak driver
3030 // c) is a higher priority strong driver
3031 // d) matches our priority, is a strong driver, and has a shorter path
3032 // e) matches our strength and is at least as short, and is alphabetically lower
3033
3035 ( !bestIsStrong && candidateStrong ) ||
3036 ( priority > highest && candidateStrong ) ||
3037 ( priority == highest && candidateStrong && shorterPath ) ||
3038 ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
3039 ( candidateName < bestName ) ) )
3040 {
3041 bestDriver = subgraph;
3042 highest = priority;
3043 bestIsStrong = candidateStrong;
3044 bestName = candidateName;
3045 }
3046 }
3047 }
3048
3049 if( bestDriver != aSubgraph )
3050 {
3051 wxLogTrace( ConnTrace, wxS( "%lu (%s) overridden by new driver %lu (%s)" ),
3052 aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
3053 bestDriver->m_driver_connection->Name() );
3054 }
3055
3056 conn = bestDriver->m_driver_connection;
3057
3058 for( CONNECTION_SUBGRAPH* subgraph : visited )
3059 {
3060 wxString old_name = subgraph->m_driver_connection->Name();
3061
3062 subgraph->m_driver_connection->Clone( *conn );
3063
3064 if( old_name != conn->Name() )
3065 recacheSubgraphName( subgraph, old_name );
3066
3067 if( conn->IsBus() )
3068 propagate_bus_neighbors( subgraph );
3069 }
3070
3071 // Somewhere along the way, a bus member may have been upgraded to a global or power label.
3072 // Because this can happen anywhere, we need a second pass to update all instances of that bus
3073 // member to have the correct connection info
3074 if( conn->IsBus() && !stale_bus_members.empty() )
3075 {
3076 std::unordered_set<SCH_CONNECTION*> cached_members = stale_bus_members;
3077
3078 for( SCH_CONNECTION* stale_member : cached_members )
3079 {
3080 for( CONNECTION_SUBGRAPH* subgraph : visited )
3081 {
3082 SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection, stale_member );
3083
3084 if( !member )
3085 {
3086 wxLogTrace( ConnTrace, wxS( "WARNING: failed to match stale member %s in %s." ),
3087 stale_member->Name(), subgraph->m_driver_connection->Name() );
3088 continue;
3089 }
3090
3091 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ), subgraph->m_code,
3092 subgraph->m_driver_connection->Name(), member->LocalName(), stale_member->Name() );
3093
3094 member->Clone( *stale_member );
3095
3096 propagate_bus_neighbors( subgraph );
3097 }
3098 }
3099 }
3100
3101 aSubgraph->m_dirty = false;
3102}
3103
3104
3105std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
3106 CONNECTION_SUBGRAPH* aSubgraph )
3107{
3108 std::shared_ptr<SCH_CONNECTION> c = std::shared_ptr<SCH_CONNECTION>( nullptr );
3109
3110 switch( aItem->Type() )
3111 {
3112 case SCH_PIN_T:
3113 if( static_cast<SCH_PIN*>( aItem )->IsPower() )
3114 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
3115
3116 break;
3117
3118 case SCH_GLOBAL_LABEL_T:
3119 case SCH_HIER_LABEL_T:
3120 case SCH_LABEL_T:
3121 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
3122 break;
3123
3124 default:
3125 break;
3126 }
3127
3128 if( c )
3129 {
3130 c->SetGraph( this );
3131 c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
3132 }
3133
3134 return c;
3135}
3136
3137
3139 SCH_CONNECTION* aSearch )
3140{
3141 if( !aBusConnection->IsBus() )
3142 return nullptr;
3143
3144 SCH_CONNECTION* match = nullptr;
3145
3146 if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
3147 {
3148 // Vector bus: compare against index, because we allow the name
3149 // to be different
3150
3151 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : aBusConnection->Members() )
3152 {
3153 if( bus_member->VectorIndex() == aSearch->VectorIndex() )
3154 {
3155 match = bus_member.get();
3156 break;
3157 }
3158 }
3159 }
3160 else
3161 {
3162 // Group bus
3163 for( const std::shared_ptr<SCH_CONNECTION>& c : aBusConnection->Members() )
3164 {
3165 // Vector inside group: compare names, because for bus groups
3166 // we expect the naming to be consistent across all usages
3167 // TODO(JE) explain this in the docs
3168 if( c->Type() == CONNECTION_TYPE::BUS )
3169 {
3170 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : c->Members() )
3171 {
3172 if( bus_member->LocalName() == aSearch->LocalName() )
3173 {
3174 match = bus_member.get();
3175 break;
3176 }
3177 }
3178 }
3179 else if( c->LocalName() == aSearch->LocalName() )
3180 {
3181 match = c.get();
3182 break;
3183 }
3184 }
3185 }
3186
3187 return match;
3188}
3189
3190
3191void CONNECTION_GRAPH::recacheSubgraphName( CONNECTION_SUBGRAPH* aSubgraph, const wxString& aOldName )
3192{
3193 auto it = m_net_name_to_subgraphs_map.find( aOldName );
3194
3195 if( it != m_net_name_to_subgraphs_map.end() )
3196 {
3197 std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
3198 std::erase( vec, aSubgraph );
3199 }
3200
3201 wxLogTrace( ConnTrace, wxS( "recacheSubgraphName: %s => %s" ), aOldName,
3202 aSubgraph->m_driver_connection->Name() );
3203
3204 m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
3205}
3206
3207
3208std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
3209{
3210 auto it = m_bus_alias_cache.find( aName );
3211
3212 return it != m_bus_alias_cache.end() ? it->second : nullptr;
3213}
3214
3215
3216std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
3217{
3218 std::vector<const CONNECTION_SUBGRAPH*> ret;
3219
3220 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3221 {
3222 // Graph is supposed to be up-to-date before calling this
3223 // Should we continue if the subgraph is not up to date?
3224 wxASSERT( !subgraph->m_dirty );
3225
3226 if( !subgraph->m_driver )
3227 continue;
3228
3229 SCH_SHEET_PATH* sheet = &subgraph->m_sheet;
3230 SCH_CONNECTION* connection = subgraph->m_driver->Connection( sheet );
3231
3232 if( !connection->IsBus() )
3233 continue;
3234
3235 auto labels = subgraph->GetVectorBusLabels();
3236
3237 if( labels.size() > 1 )
3238 {
3239 bool different = false;
3240 wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText( sheet, false );
3241
3242 for( unsigned i = 1; i < labels.size(); ++i )
3243 {
3244 if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText( sheet, false ) != first )
3245 {
3246 different = true;
3247 break;
3248 }
3249 }
3250
3251 if( !different )
3252 continue;
3253
3254 wxLogTrace( ConnTrace, wxS( "SG %ld (%s) has multiple bus labels" ), subgraph->m_code,
3255 connection->Name() );
3256
3257 ret.push_back( subgraph );
3258 }
3259 }
3260
3261 return ret;
3262}
3263
3264
3266{
3267 wxString retval = aSubGraph->GetNetName();
3268 bool found = false;
3269
3270 // This is a hacky way to find the true subgraph net name (why do we not store it?)
3271 // TODO: Remove once the actual netname of the subgraph is stored with the subgraph
3272
3273 for( auto it = m_net_name_to_subgraphs_map.begin();
3274 it != m_net_name_to_subgraphs_map.end() && !found; ++it )
3275 {
3276 for( CONNECTION_SUBGRAPH* graph : it->second )
3277 {
3278 if( graph == aSubGraph )
3279 {
3280 retval = it->first;
3281 found = true;
3282 break;
3283 }
3284 }
3285 }
3286
3287 return retval;
3288}
3289
3290
3292 const SCH_SHEET_PATH& aPath )
3293{
3294 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3295
3296 if( it == m_net_name_to_subgraphs_map.end() )
3297 return nullptr;
3298
3299 for( CONNECTION_SUBGRAPH* sg : it->second )
3300 {
3301 // Cache is supposed to be valid by now
3302 // Should we continue if the cache is not valid?
3303 wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
3304
3305 if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
3306 return sg;
3307 }
3308
3309 return nullptr;
3310}
3311
3312
3314{
3315 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3316
3317 if( it == m_net_name_to_subgraphs_map.end() )
3318 return nullptr;
3319
3320 // Should this return a nullptr if the map entry is empty?
3321 wxASSERT( !it->second.empty() );
3322
3323 return it->second[0];
3324}
3325
3326
3328{
3329 auto it = m_item_to_subgraph_map.find( aItem );
3330 CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
3331
3332 while( ret && ret->m_absorbed )
3333 ret = ret->m_absorbed_by;
3334
3335 return ret;
3336}
3337
3338
3339const std::vector<CONNECTION_SUBGRAPH*>&
3340CONNECTION_GRAPH::GetAllSubgraphs( const wxString& aNetName ) const
3341{
3342 static const std::vector<CONNECTION_SUBGRAPH*> subgraphs;
3343
3344 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3345
3346 if( it == m_net_name_to_subgraphs_map.end() )
3347 return subgraphs;
3348
3349 return it->second;
3350}
3351
3352
3354{
3355 int error_count = 0;
3356
3357 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::RunERC" ) );
3358
3359 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3360
3361 // We don't want to run many ERC checks more than once on a given screen even though it may
3362 // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
3363 std::set<SCH_ITEM*> seenDriverInstances;
3364
3365 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3366 {
3367 // There shouldn't be any null sub-graph pointers.
3368 wxCHECK2( subgraph, continue );
3369
3370 // Graph is supposed to be up-to-date before calling RunERC()
3371 // Should we continue if the subgraph is not up to date?
3372 wxASSERT( !subgraph->m_dirty );
3373
3374 if( subgraph->m_absorbed )
3375 continue;
3376
3377 if( seenDriverInstances.count( subgraph->m_driver ) )
3378 continue;
3379
3380 if( subgraph->m_driver )
3381 seenDriverInstances.insert( subgraph->m_driver );
3382
3393 if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
3394 {
3395 if( !ercCheckMultipleDrivers( subgraph ) )
3396 error_count++;
3397 }
3398
3399 subgraph->ResolveDrivers( false );
3400
3401 if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
3402 {
3403 if( !ercCheckBusToNetConflicts( subgraph ) )
3404 error_count++;
3405 }
3406
3407 if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
3408 {
3409 if( !ercCheckBusToBusEntryConflicts( subgraph ) )
3410 error_count++;
3411 }
3412
3413 if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
3414 {
3415 if( !ercCheckBusToBusConflicts( subgraph ) )
3416 error_count++;
3417 }
3418
3419 if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
3420 {
3421 if( !ercCheckFloatingWires( subgraph ) )
3422 error_count++;
3423 }
3424
3426 {
3427 if( !ercCheckDanglingWireEndpoints( subgraph ) )
3428 error_count++;
3429 }
3430
3433 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3434 {
3435 if( !ercCheckNoConnects( subgraph ) )
3436 error_count++;
3437 }
3438
3440 || settings.IsTestEnabled( ERCE_LABEL_SINGLE_PIN ) )
3441 {
3442 if( !ercCheckLabels( subgraph ) )
3443 error_count++;
3444 }
3445 }
3446
3447 if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
3448 {
3449 error_count += ercCheckDirectiveLabels();
3450 }
3451
3452 // Hierarchical sheet checking is done at the schematic level
3454 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3455 {
3456 error_count += ercCheckHierSheets();
3457 }
3458
3459 if( settings.IsTestEnabled( ERCE_SINGLE_GLOBAL_LABEL ) )
3460 {
3461 error_count += ercCheckSingleGlobalLabel();
3462 }
3463
3464 return error_count;
3465}
3466
3467
3469{
3470 wxCHECK( aSubgraph, false );
3471
3472 if( aSubgraph->m_multiple_drivers )
3473 {
3474 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3475 {
3476 if( driver == aSubgraph->m_driver )
3477 continue;
3478
3479 if( driver->Type() == SCH_GLOBAL_LABEL_T
3480 || driver->Type() == SCH_HIER_LABEL_T
3481 || driver->Type() == SCH_LABEL_T
3482 || ( driver->Type() == SCH_PIN_T && static_cast<SCH_PIN*>( driver )->IsPower() ) )
3483 {
3484 const wxString& primaryName = aSubgraph->GetNameForDriver( aSubgraph->m_driver );
3485 const wxString& secondaryName = aSubgraph->GetNameForDriver( driver );
3486
3487 if( primaryName == secondaryName )
3488 continue;
3489
3490 wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
3491 "items; %s will be used in the netlist" ),
3492 primaryName, secondaryName, primaryName );
3493
3494 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
3495 ercItem->SetItems( aSubgraph->m_driver, driver );
3496 ercItem->SetSheetSpecificPath( aSubgraph->GetSheet() );
3497 ercItem->SetItemsSheetPaths( aSubgraph->GetSheet(), aSubgraph->m_sheet );
3498 ercItem->SetErrorMessage( msg );
3499
3500 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), driver->GetPosition() );
3501 aSubgraph->m_sheet.LastScreen()->Append( marker );
3502
3503 return false;
3504 }
3505 }
3506 }
3507
3508 return true;
3509}
3510
3511
3513{
3514 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3515 SCH_SCREEN* screen = sheet.LastScreen();
3516
3517 SCH_ITEM* net_item = nullptr;
3518 SCH_ITEM* bus_item = nullptr;
3519 SCH_CONNECTION conn( this );
3520
3521 for( SCH_ITEM* item : aSubgraph->m_items )
3522 {
3523 switch( item->Type() )
3524 {
3525 case SCH_LINE_T:
3526 {
3527 if( item->GetLayer() == LAYER_BUS )
3528 bus_item = ( !bus_item ) ? item : bus_item;
3529 else
3530 net_item = ( !net_item ) ? item : net_item;
3531
3532 break;
3533 }
3534
3535 case SCH_LABEL_T:
3536 case SCH_GLOBAL_LABEL_T:
3537 case SCH_SHEET_PIN_T:
3538 case SCH_HIER_LABEL_T:
3539 {
3540 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3541 conn.ConfigureFromLabel( EscapeString( text->GetShownText( &sheet, false ), CTX_NETNAME ) );
3542
3543 if( conn.IsBus() )
3544 bus_item = ( !bus_item ) ? item : bus_item;
3545 else
3546 net_item = ( !net_item ) ? item : net_item;
3547
3548 break;
3549 }
3550
3551 default:
3552 break;
3553 }
3554 }
3555
3556 if( net_item && bus_item )
3557 {
3558 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
3559 ercItem->SetSheetSpecificPath( sheet );
3560 ercItem->SetItems( net_item, bus_item );
3561
3562 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), net_item->GetPosition() );
3563 screen->Append( marker );
3564
3565 return false;
3566 }
3567
3568 return true;
3569}
3570
3571
3573{
3574 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3575 SCH_SCREEN* screen = sheet.LastScreen();
3576
3577 SCH_ITEM* label = nullptr;
3578 SCH_ITEM* port = nullptr;
3579
3580 for( SCH_ITEM* item : aSubgraph->m_items )
3581 {
3582 switch( item->Type() )
3583 {
3584 case SCH_TEXT_T:
3585 case SCH_GLOBAL_LABEL_T:
3586 if( !label && item->Connection( &sheet )->IsBus() )
3587 label = item;
3588 break;
3589
3590 case SCH_SHEET_PIN_T:
3591 case SCH_HIER_LABEL_T:
3592 if( !port && item->Connection( &sheet )->IsBus() )
3593 port = item;
3594 break;
3595
3596 default:
3597 break;
3598 }
3599 }
3600
3601 if( label && port )
3602 {
3603 bool match = false;
3604
3605 for( const auto& member : label->Connection( &sheet )->Members() )
3606 {
3607 for( const auto& test : port->Connection( &sheet )->Members() )
3608 {
3609 if( test != member && member->Name() == test->Name() )
3610 {
3611 match = true;
3612 break;
3613 }
3614 }
3615
3616 if( match )
3617 break;
3618 }
3619
3620 if( !match )
3621 {
3622 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
3623 ercItem->SetSheetSpecificPath( sheet );
3624 ercItem->SetItems( label, port );
3625
3626 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), label->GetPosition() );
3627 screen->Append( marker );
3628
3629 return false;
3630 }
3631 }
3632
3633 return true;
3634}
3635
3636
3638{
3639 bool conflict = false;
3640 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3641 SCH_SCREEN* screen = sheet.LastScreen();
3642
3643 SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
3644 SCH_ITEM* bus_wire = nullptr;
3645 wxString bus_name;
3646
3647 if( !aSubgraph->m_driver_connection )
3648 {
3649 // Incomplete bus entry. Let the unconnected tests handle it.
3650 return true;
3651 }
3652
3653 for( SCH_ITEM* item : aSubgraph->m_items )
3654 {
3655 switch( item->Type() )
3656 {
3658 if( !bus_entry )
3659 bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
3660
3661 break;
3662
3663 default:
3664 break;
3665 }
3666 }
3667
3668 if( bus_entry && bus_entry->m_connected_bus_item )
3669 {
3670 bus_wire = bus_entry->m_connected_bus_item;
3671
3672 // Should we continue if the type is not a line?
3673 wxASSERT( bus_wire->Type() == SCH_LINE_T );
3674
3675 // In some cases, the connection list (SCH_CONNECTION*) can be null.
3676 // Skip null connections.
3677 if( bus_entry->Connection( &sheet )
3678 && bus_wire->Type() == SCH_LINE_T
3679 && bus_wire->Connection( &sheet ) )
3680 {
3681 conflict = true; // Assume a conflict; we'll reset if we find it's OK
3682
3683 bus_name = bus_wire->Connection( &sheet )->Name();
3684
3685 std::set<wxString> test_names;
3686 test_names.insert( bus_entry->Connection( &sheet )->FullLocalName() );
3687
3688 wxString baseName = sheet.PathHumanReadable();
3689
3690 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3691 test_names.insert( baseName + aSubgraph->GetNameForDriver( driver ) );
3692
3693 for( const auto& member : bus_wire->Connection( &sheet )->Members() )
3694 {
3695 if( member->Type() == CONNECTION_TYPE::BUS )
3696 {
3697 for( const auto& sub_member : member->Members() )
3698 {
3699 if( test_names.count( sub_member->FullLocalName() ) )
3700 conflict = false;
3701 }
3702 }
3703 else if( test_names.count( member->FullLocalName() ) )
3704 {
3705 conflict = false;
3706 }
3707 }
3708 }
3709 }
3710
3711 // Don't report warnings if this bus member has been overridden by a higher priority power pin
3712 // or global label
3713 if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
3715 {
3716 conflict = false;
3717 }
3718
3719 if( conflict )
3720 {
3721 wxString netName = aSubgraph->m_driver_connection->Name();
3722 wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
3723 " member of that bus" ),
3724 UnescapeString( netName ),
3725 UnescapeString( bus_name ) );
3726 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
3727 ercItem->SetSheetSpecificPath( sheet );
3728 ercItem->SetItems( bus_entry, bus_wire );
3729 ercItem->SetErrorMessage( msg );
3730
3731 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), bus_entry->GetPosition() );
3732 screen->Append( marker );
3733
3734 return false;
3735 }
3736
3737 return true;
3738}
3739
3740
3742{
3743 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3744 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3745 SCH_SCREEN* screen = sheet.LastScreen();
3746 bool ok = true;
3747 SCH_PIN* pin = nullptr;
3748
3749 std::set<SCH_PIN*> unique_pins;
3750 std::set<SCH_LABEL_BASE*> unique_labels;
3751
3752 wxString netName = GetResolvedSubgraphName( aSubgraph );
3753
3754 auto process_subgraph = [&]( const CONNECTION_SUBGRAPH* aProcessGraph )
3755 {
3756 // Any subgraph that contains a no-connect should not
3757 // more than one pin (which would indicate it is connected
3758 for( SCH_ITEM* item : aProcessGraph->m_items )
3759 {
3760 switch( item->Type() )
3761 {
3762 case SCH_PIN_T:
3763 {
3764 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3765
3766 // Only link NC to pin on the current subgraph being checked
3767 if( aProcessGraph == aSubgraph )
3768 pin = test_pin;
3769
3770 if( std::none_of( unique_pins.begin(), unique_pins.end(),
3771 [test_pin]( SCH_PIN* aPin )
3772 {
3773 return test_pin->IsStacked( aPin );
3774 }
3775 ))
3776 {
3777 unique_pins.insert( test_pin );
3778 }
3779
3780 break;
3781 }
3782
3783 case SCH_LABEL_T:
3784 case SCH_GLOBAL_LABEL_T:
3785 case SCH_HIER_LABEL_T:
3786 unique_labels.insert( static_cast<SCH_LABEL_BASE*>( item ) );
3788 default:
3789 break;
3790 }
3791 }
3792 };
3793
3794 auto it = m_net_name_to_subgraphs_map.find( netName );
3795
3796 if( it != m_net_name_to_subgraphs_map.end() )
3797 {
3798 for( const CONNECTION_SUBGRAPH* subgraph : it->second )
3799 {
3800 process_subgraph( subgraph );
3801 }
3802 }
3803 else
3804 {
3805 process_subgraph( aSubgraph );
3806 }
3807
3808 if( aSubgraph->m_no_connect != nullptr )
3809 {
3810 // Special case: If the subgraph being checked consists of only a hier port/pin and
3811 // a no-connect, we don't issue a "no-connect connected" warning just because
3812 // connections exist on the sheet on the other side of the link.
3813 VECTOR2I noConnectPos = aSubgraph->m_no_connect->GetPosition();
3814
3815 for( SCH_SHEET_PIN* hierPin : aSubgraph->m_hier_pins )
3816 {
3817 if( hierPin->GetPosition() == noConnectPos )
3818 return true;
3819 }
3820
3821 for( SCH_HIERLABEL* hierLabel : aSubgraph->m_hier_ports )
3822 {
3823 if( hierLabel->GetPosition() == noConnectPos )
3824 return true;
3825 }
3826
3827 for( SCH_ITEM* item : screen->Items().Overlapping( SCH_SYMBOL_T, noConnectPos ) )
3828 {
3829 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3830
3831 const SCH_PIN* test_pin = symbol->GetPin( noConnectPos );
3832
3833 if( test_pin && test_pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
3834 return true;
3835 }
3836
3837 if( unique_pins.size() > 1 && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
3838 {
3839 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
3840 ercItem->SetSheetSpecificPath( sheet );
3841 ercItem->SetItemsSheetPaths( sheet );
3842
3843 VECTOR2I pos;
3844
3845 if( pin )
3846 {
3847 ercItem->SetItems( pin, aSubgraph->m_no_connect );
3848 pos = pin->GetPosition();
3849 }
3850 else
3851 {
3852 ercItem->SetItems( aSubgraph->m_no_connect );
3853 pos = aSubgraph->m_no_connect->GetPosition();
3854 }
3855
3856 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pos );
3857 screen->Append( marker );
3858
3859 ok = false;
3860 }
3861
3862 if( unique_pins.empty() && unique_labels.empty() &&
3864 {
3865 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
3866 ercItem->SetItems( aSubgraph->m_no_connect );
3867 ercItem->SetSheetSpecificPath( sheet );
3868 ercItem->SetItemsSheetPaths( sheet );
3869
3870 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), aSubgraph->m_no_connect->GetPosition() );
3871 screen->Append( marker );
3872
3873 ok = false;
3874 }
3875 }
3876 else
3877 {
3878 bool has_other_connections = false;
3879 std::vector<SCH_PIN*> pins;
3880
3881 // Any subgraph that lacks a no-connect and contains a pin should also
3882 // contain at least one other potential driver
3883
3884 for( SCH_ITEM* item : aSubgraph->m_items )
3885 {
3886 switch( item->Type() )
3887 {
3888 case SCH_PIN_T:
3889 {
3890 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3891
3892 // Stacked pins do not count as other connections but non-stacked pins do
3893 if( !has_other_connections && !pins.empty()
3894 && !test_pin->GetParentSymbol()->IsPower() )
3895 {
3896 for( SCH_PIN* other_pin : pins )
3897 {
3898 if( !test_pin->IsStacked( other_pin ) )
3899 {
3900 has_other_connections = true;
3901 break;
3902 }
3903 }
3904 }
3905
3906 pins.emplace_back( static_cast<SCH_PIN*>( item ) );
3907
3908 break;
3909 }
3910
3911 default:
3912 if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
3913 has_other_connections = true;
3914
3915 break;
3916 }
3917 }
3918
3919 // For many checks, we can just use the first pin
3920 pin = pins.empty() ? nullptr : pins[0];
3921
3922 // But if there is a power pin, it might be connected elsewhere
3923 for( SCH_PIN* test_pin : pins )
3924 {
3925 // Prefer the pin is part of a real component rather than some stray power symbol
3926 // Or else we may fail walking connected components to a power symbol pin since we
3927 // reject starting at a power symbol
3928 if( test_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && !test_pin->IsPower() )
3929 {
3930 pin = test_pin;
3931 break;
3932 }
3933 }
3934
3935 // Check if power input pins connect to anything else via net name,
3936 // but not for power symbols (with visible or legacy invisible pins).
3937 // We want to throw unconnected errors for power symbols even if they are connected to other
3938 // net items by name, because usually failing to connect them graphically is a mistake
3939 SYMBOL* pinLibParent = ( pin && pin->GetLibPin() )
3940 ? pin->GetLibPin()->GetParentSymbol() : nullptr;
3941
3942 if( pin && !has_other_connections
3943 && !pin->IsPower()
3944 && ( !pinLibParent || !pinLibParent->IsPower() ) )
3945 {
3946 wxString name = pin->Connection( &sheet )->Name();
3947 wxString local_name = pin->Connection( &sheet )->Name( true );
3948
3949 if( m_global_label_cache.count( name )
3950 || m_local_label_cache.count( std::make_pair( sheet, local_name ) ) )
3951 {
3952 has_other_connections = true;
3953 }
3954 }
3955
3956 // Only one pin, and it's not a no-connect pin
3957 if( pin && !has_other_connections
3958 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
3959 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
3960 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3961 {
3962 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3963 ercItem->SetSheetSpecificPath( sheet );
3964 ercItem->SetItemsSheetPaths( sheet );
3965 ercItem->SetItems( pin );
3966
3967 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pin->GetPosition() );
3968 screen->Append( marker );
3969
3970 ok = false;
3971 }
3972
3973 // If there are multiple pins in this SG, they might be indirectly connected (by netname)
3974 // rather than directly connected (by wires). We want to flag dangling pins even if they
3975 // join nets with another pin, as it's often a mistake
3976 if( pins.size() > 1 )
3977 {
3978 for( SCH_PIN* testPin : pins )
3979 {
3980 // We only apply this test to power symbols, because other symbols have
3981 // pins that are meant to be dangling, but the power symbols have pins
3982 // that are *not* meant to be dangling.
3983 SYMBOL* testLibParent = testPin->GetLibPin()
3984 ? testPin->GetLibPin()->GetParentSymbol()
3985 : nullptr;
3986
3987 if( testLibParent && testLibParent->IsPower()
3988 && testPin->ConnectedItems( sheet ).empty()
3989 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3990 {
3991 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3992 ercItem->SetSheetSpecificPath( sheet );
3993 ercItem->SetItemsSheetPaths( sheet );
3994 ercItem->SetItems( testPin );
3995
3996 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), testPin->GetPosition() );
3997 screen->Append( marker );
3998
3999 ok = false;
4000 }
4001 }
4002 }
4003 }
4004
4005 return ok;
4006}
4007
4008
4010{
4011 int err_count = 0;
4012 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
4013
4014 for( SCH_ITEM* item : aSubgraph->m_items )
4015 {
4016 if( item->GetLayer() != LAYER_WIRE )
4017 continue;
4018
4019 if( item->Type() == SCH_LINE_T )
4020 {
4021 SCH_LINE* line = static_cast<SCH_LINE*>( item );
4022
4023 if( line->IsGraphicLine() )
4024 continue;
4025
4026 auto report_error = [&]( VECTOR2I& location )
4027 {
4028 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_UNCONNECTED_WIRE_ENDPOINT );
4029
4030 ercItem->SetItems( line );
4031 ercItem->SetSheetSpecificPath( sheet );
4032 ercItem->SetErrorMessage( _( "Unconnected wire endpoint" ) );
4033
4034 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), location );
4035 sheet.LastScreen()->Append( marker );
4036
4037 err_count++;
4038 };
4039
4040 if( line->IsStartDangling() )
4041 report_error( line->GetConnectionPoints()[0] );
4042
4043 if( line->IsEndDangling() )
4044 report_error( line->GetConnectionPoints()[1] );
4045 }
4046 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
4047 {
4048 SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
4049
4050 auto report_error = [&]( VECTOR2I& location )
4051 {
4052 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_UNCONNECTED_WIRE_ENDPOINT );
4053
4054 ercItem->SetItems( entry );
4055 ercItem->SetSheetSpecificPath( sheet );
4056 ercItem->SetErrorMessage( _( "Unconnected wire to bus entry" ) );
4057
4058 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), location );
4059 sheet.LastScreen()->Append( marker );
4060
4061 err_count++;
4062 };
4063
4064 if( entry->IsStartDangling() )
4065 report_error( entry->GetConnectionPoints()[0] );
4066
4067 if( entry->IsEndDangling() )
4068 report_error( entry->GetConnectionPoints()[1] );
4069 }
4070
4071 }
4072
4073 return err_count > 0;
4074}
4075
4076
4078{
4079 if( aSubgraph->m_driver )
4080 return true;
4081
4082 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
4083 std::vector<SCH_ITEM*> wires;
4084
4085 // We've gotten this far, so we know we have no valid driver. All we need to do is check
4086 // for a wire that we can place the error on.
4087 for( SCH_ITEM* item : aSubgraph->m_items )
4088 {
4089 if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
4090 wires.emplace_back( item );
4091 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
4092 wires.emplace_back( item );
4093 }
4094
4095 if( !wires.empty() )
4096 {
4097 SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
4098
4099 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
4100 ercItem->SetSheetSpecificPath( sheet );
4101 ercItem->SetItems( wires[0],
4102 wires.size() > 1 ? wires[1] : nullptr,
4103 wires.size() > 2 ? wires[2] : nullptr,
4104 wires.size() > 3 ? wires[3] : nullptr );
4105
4106 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), wires[0]->GetPosition() );
4107 screen->Append( marker );
4108
4109 return false;
4110 }
4111
4112 return true;
4113}
4114
4115
4117{
4118 // Label connection rules:
4119 // Any label without a no-connect needs to have at least 2 pins, otherwise it is invalid
4120 // Local labels are flagged if they don't connect to any pins and don't have a no-connect
4121 // Global labels are flagged if they appear only once, don't connect to any local labels,
4122 // and don't have a no-connect marker
4123
4124 if( !aSubgraph->m_driver_connection )
4125 return true;
4126
4127 // Buses are excluded from this test: many users create buses with only a single instance
4128 // and it's not really a problem as long as the nets in the bus pass ERC
4129 if( aSubgraph->m_driver_connection->IsBus() )
4130 return true;
4131
4132 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
4133 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4134 bool ok = true;
4135 size_t pinCount = 0;
4136 bool has_nc = !!aSubgraph->m_no_connect;
4137
4138 std::map<KICAD_T, std::vector<SCH_TEXT*>> label_map;
4139
4140
4141 auto hasPins =
4142 []( const CONNECTION_SUBGRAPH* aLocSubgraph ) -> size_t
4143 {
4144 return std::count_if( aLocSubgraph->m_items.begin(), aLocSubgraph->m_items.end(),
4145 []( const SCH_ITEM* item )
4146 {
4147 return item->Type() == SCH_PIN_T;
4148 } );
4149 };
4150
4151 auto reportError =
4152 [&]( SCH_TEXT* aText, int errCode )
4153 {
4154 if( settings.IsTestEnabled( errCode ) )
4155 {
4156 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
4157 ercItem->SetSheetSpecificPath( sheet );
4158 ercItem->SetItems( aText );
4159
4160 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), aText->GetPosition() );
4161 aSubgraph->m_sheet.LastScreen()->Append( marker );
4162 }
4163 };
4164
4165 pinCount = hasPins( aSubgraph );
4166
4167 for( SCH_ITEM* item : aSubgraph->m_items )
4168 {
4169 switch( item->Type() )
4170 {
4171 case SCH_LABEL_T:
4172 case SCH_GLOBAL_LABEL_T:
4173 case SCH_HIER_LABEL_T:
4174 {
4175 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
4176
4177 label_map[item->Type()].push_back( text );
4178
4179 // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
4180 // we want to error if an individual label in the subgraph is floating, even if it's
4181 // connected to other valid things by way of another label on the same sheet.
4182 if( text->IsDangling() )
4183 {
4184 reportError( text, ERCE_LABEL_NOT_CONNECTED );
4185 return false;
4186 }
4187
4188 break;
4189 }
4190
4191 default:
4192 break;
4193 }
4194 }
4195
4196 if( label_map.empty() )
4197 return true;
4198
4199 // No-connects on net neighbors will be noticed before, but to notice them on bus parents we
4200 // need to walk the graph
4201 for( auto& [ connection, subgraphs ] : aSubgraph->m_bus_parents )
4202 {
4203 for( CONNECTION_SUBGRAPH* busParent : subgraphs )
4204 {
4205 if( busParent->m_no_connect )
4206 {
4207 has_nc = true;
4208 break;
4209 }
4210
4211 CONNECTION_SUBGRAPH* hp = busParent->m_hier_parent;
4212
4213 while( hp )
4214 {
4215 if( hp->m_no_connect )
4216 {
4217 has_nc = true;
4218 break;
4219 }
4220
4221 hp = hp->m_hier_parent;
4222 }
4223 }
4224 }
4225
4226 wxString netName = GetResolvedSubgraphName( aSubgraph );
4227
4228 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" ) );
4229
4230 // Labels that have multiple pins connected are not dangling (may be used for naming segments)
4231 // so leave them without errors here
4232 if( pinCount > 1 )
4233 return true;
4234
4235 for( auto& [type, label_vec] : label_map )
4236 {
4237 for( SCH_TEXT* text : label_vec )
4238 {
4239 size_t allPins = pinCount;
4240 size_t localPins = pinCount;
4241
4242 auto it = m_net_name_to_subgraphs_map.find( netName );
4243
4244 if( it != m_net_name_to_subgraphs_map.end() )
4245 {
4246 for( const CONNECTION_SUBGRAPH* neighbor : it->second )
4247 {
4248 if( neighbor == aSubgraph )
4249 continue;
4250
4251 if( neighbor->m_no_connect )
4252 has_nc = true;
4253
4254 size_t neighborPins = hasPins( neighbor );
4255 allPins += neighborPins;
4256
4257 if( neighbor->m_sheet == sheet )
4258 localPins += neighborPins;
4259 }
4260 }
4261
4262 if( allPins == 1 && !has_nc )
4263 {
4264 reportError( text, ERCE_LABEL_SINGLE_PIN );
4265 ok = false;
4266 }
4267
4268 // A local label should connect to at least one component pin on its own
4269 // sheet. A label that only reaches pins through hierarchy (sheet pins or
4270 // buses) serves no local purpose and is likely mislabeled.
4271 if( allPins == 0
4272 || ( type == SCH_LABEL_T && localPins == 0 && allPins > 1 && !has_nc ) )
4273 {
4274 reportError( text, ERCE_LABEL_NOT_CONNECTED );
4275 ok = false;
4276 }
4277 }
4278 }
4279
4280 return ok;
4281}
4282
4283
4285{
4286 int errors = 0;
4287
4288 std::map<wxString, std::tuple<int, const SCH_ITEM*, SCH_SHEET_PATH>> labelData;
4289
4290 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4291 {
4292 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
4293 {
4294 SCH_TEXT* labelText = static_cast<SCH_TEXT*>( item );
4295 wxString resolvedLabelText =
4296 EscapeString( labelText->GetShownText( &sheet, false ), CTX_NETNAME );
4297
4298 if( labelData.find( resolvedLabelText ) == labelData.end() )
4299 {
4300 labelData[resolvedLabelText] = { 1, item, sheet };
4301 }
4302 else
4303 {
4304 std::get<0>( labelData[resolvedLabelText] ) += 1;
4305 std::get<1>( labelData[resolvedLabelText] ) = nullptr;
4306 std::get<2>( labelData[resolvedLabelText] ) = sheet;
4307 }
4308 }
4309 }
4310
4311 for( const auto& label : labelData )
4312 {
4313 if( std::get<0>( label.second ) == 1 )
4314 {
4315 const SCH_SHEET_PATH& sheet = std::get<2>( label.second );
4316 const SCH_ITEM* item = std::get<1>( label.second );
4317
4318 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SINGLE_GLOBAL_LABEL );
4319 ercItem->SetItems( std::get<1>( label.second ) );
4320 ercItem->SetSheetSpecificPath( sheet );
4321 ercItem->SetItemsSheetPaths( sheet );
4322
4323 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), item->GetPosition() );
4324 sheet.LastScreen()->Append( marker );
4325
4326 errors++;
4327 }
4328 }
4329
4330 return errors;
4331}
4332
4333
4335{
4336 int error_count = 0;
4337
4338 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4339 {
4340 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_DIRECTIVE_LABEL_T ) )
4341 {
4342 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
4343
4344 if( label->IsDangling() )
4345 {
4346 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LABEL_NOT_CONNECTED );
4347 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
4348 ercItem->SetSheetSpecificPath( sheet );
4349 ercItem->SetItems( text );
4350
4351 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), text->GetPosition() );
4352 sheet.LastScreen()->Append( marker );
4353 error_count++;
4354 }
4355 }
4356 }
4357
4358 return error_count;
4359}
4360
4361
4363{
4364 wxString msg;
4365 int errors = 0;
4366
4367 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4368
4369 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4370 {
4371 // Hierarchical labels in the top-level sheets cannot be connected to anything.
4372 if( sheet.Last()->IsTopLevelSheet() )
4373 {
4374 for( const SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
4375 {
4376 const SCH_HIERLABEL* label = static_cast<const SCH_HIERLABEL*>( item );
4377
4378 wxCHECK2( label, continue );
4379
4380 msg.Printf( _( "Hierarchical label '%s' in root sheet cannot be connected to non-existent "
4381 "parent sheet" ),
4382 label->GetShownText( &sheet, true ) );
4383 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4384 ercItem->SetItems( item );
4385 ercItem->SetErrorMessage( msg );
4386
4387 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), item->GetPosition() );
4388 sheet.LastScreen()->Append( marker );
4389
4390 errors++;
4391 }
4392 }
4393
4394 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
4395 {
4396 SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
4397 SCH_SHEET_PATH parentSheetPath = sheet;
4398
4399 parentSheetPath.push_back( parentSheet );
4400
4401 std::map<wxString, SCH_SHEET_PIN*> pins;
4402 std::map<wxString, SCH_HIERLABEL*> labels;
4403
4404 for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
4405 {
4406 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4407 pins[ pin->GetShownText( &parentSheetPath, false ) ] = pin;
4408
4409 if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
4410 {
4411 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4412 ercItem->SetItems( pin );
4413 ercItem->SetSheetSpecificPath( sheet );
4414 ercItem->SetItemsSheetPaths( sheet );
4415
4416 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pin->GetPosition() );
4417 sheet.LastScreen()->Append( marker );
4418
4419 errors++;
4420 }
4421 }
4422
4423 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4424 {
4425 std::set<wxString> matchedPins;
4426
4427 for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
4428 {
4429 if( subItem->Type() == SCH_HIER_LABEL_T )
4430 {
4431 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
4432 wxString labelText = label->GetShownText( &parentSheetPath, false );
4433
4434 if( !pins.contains( labelText ) )
4435 labels[ labelText ] = label;
4436 else
4437 matchedPins.insert( labelText );
4438 }
4439 }
4440
4441 for( const wxString& matched : matchedPins )
4442 pins.erase( matched );
4443
4444 for( const auto& [name, pin] : pins )
4445 {
4446 msg.Printf( _( "Sheet pin %s has no matching hierarchical label inside the sheet" ),
4447 UnescapeString( name ) );
4448
4449 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4450 ercItem->SetItems( pin );
4451 ercItem->SetErrorMessage( msg );
4452 ercItem->SetSheetSpecificPath( sheet );
4453 ercItem->SetItemsSheetPaths( sheet );
4454
4455 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pin->GetPosition() );
4456 sheet.LastScreen()->Append( marker );
4457
4458 errors++;
4459 }
4460
4461 for( const auto& [name, label] : labels )
4462 {
4463 msg.Printf( _( "Hierarchical label %s has no matching sheet pin in the parent sheet" ),
4464 UnescapeString( name ) );
4465
4466 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4467 ercItem->SetItems( label );
4468 ercItem->SetErrorMessage( msg );
4469 ercItem->SetSheetSpecificPath( parentSheetPath );
4470 ercItem->SetItemsSheetPaths( parentSheetPath );
4471
4472 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), label->GetPosition() );
4473 parentSheet->GetScreen()->Append( marker );
4474
4475 errors++;
4476 }
4477 }
4478 }
4479 }
4480
4481 return errors;
4482}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
This represents a sentry transaction which is used for time-performance metrics You start a transacti...
Definition app_monitor.h:64
void StartSpan(const std::string &aOperation, const std::string &aDescription)
int RunERC()
Run electrical rule checks on the connectivity graph.
bool ercCheckBusToBusConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for conflicting connections between two bus items.
void processSubGraphs()
Process all subgraphs to assign netcodes and merge subgraphs based on labels.
bool ercCheckLabels(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for proper connection of labels.
void RemoveItem(SCH_ITEM *aItem)
void collectAllDriverValues()
Map the driver values for each subgraph.
CONNECTION_SUBGRAPH * FindSubgraphByName(const wxString &aNetName, const SCH_SHEET_PATH &aPath)
Return the subgraph for a given net name on a given sheet.
int ercCheckDirectiveLabels()
Check directive labels should be connected to something.
void recacheSubgraphName(CONNECTION_SUBGRAPH *aSubgraph, const wxString &aOldName)
static SCH_CONNECTION * matchBusMember(SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
Search for a matching bus member inside a bus connection.
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
SCHEMATIC * m_schematic
The schematic this graph represents.
void updateGenericItemConnectivity(const SCH_SHEET_PATH &aSheet, SCH_ITEM *aItem, std::map< VECTOR2I, std::vector< SCH_ITEM * > > &aConnectionMap)
Update the connectivity of items that are not pins or symbols.
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
Cache to lookup subgraphs in m_driver_subgraphs by sheet path.
void updateSymbolConnectivity(const SCH_SHEET_PATH &aSheet, SCH_SYMBOL *aSymbol, std::map< VECTOR2I, std::vector< SCH_ITEM * > > &aConnectionMap)
Update the connectivity of a symbol and its pins.
CONNECTION_SUBGRAPH * FindFirstSubgraphByName(const wxString &aNetName)
Retrieve a subgraph for the given net name, if one exists.
void propagateToNeighbors(CONNECTION_SUBGRAPH *aSubgraph, bool aForce)
Update all neighbors of a subgraph with this one's connectivity info.
void buildItemSubGraphs()
Generate individual item subgraphs on a per-sheet basis.
const std::vector< CONNECTION_SUBGRAPH * > & GetAllSubgraphs(const wxString &aNetName) const
bool ercCheckMultipleDrivers(const CONNECTION_SUBGRAPH *aSubgraph)
If the subgraph has multiple drivers of equal priority that are graphically connected,...
SCH_SHEET_LIST m_sheetList
All the sheets in the schematic (as long as we don't have partial updates).
void generateGlobalPowerPinSubGraphs()
Iterate through the global power pins to collect the global labels as drivers.
std::unordered_map< wxString, int > m_net_name_to_code_map
int ercCheckSingleGlobalLabel()
Check that a global label is instantiated more that once across the schematic hierarchy.
int ercCheckHierSheets()
Check that a hierarchical sheet has at least one matching label inside the sheet for each port on the...
bool ercCheckBusToNetConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for conflicting connections between net and bus labels.
std::shared_ptr< SCH_CONNECTION > getDefaultConnection(SCH_ITEM *aItem, CONNECTION_SUBGRAPH *aSubgraph)
Build a new default connection for the given item based on its properties.
std::vector< const CONNECTION_SUBGRAPH * > GetBusesNeedingMigration()
Determine which subgraphs have more than one conflicting bus label.
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Update the connection graph for the given list of sheets.
int assignNewNetCode(SCH_CONNECTION &aConnection)
Helper to assign a new net code to a connection.
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
int getOrCreateNetCode(const wxString &aNetName)
bool ercCheckDanglingWireEndpoints(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for dangling wire endpoints.
void assignNetCodesToBus(SCH_CONNECTION *aConnection)
Ensure all members of the bus connection have a valid net code assigned.
std::unordered_map< wxString, int > m_bus_name_to_code_map
std::unordered_map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
The owner of all CONNECTION_SUBGRAPH objects.
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_global_power_pins
CONNECTION_GRAPH(SCHEMATIC *aSchematic=nullptr)
bool ercCheckNoConnects(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for proper presence or absence of no-connect symbols.
size_t hasPins(const CONNECTION_SUBGRAPH *aLocSubgraph)
Get the number of pins in a given subgraph.
std::vector< SCH_ITEM * > m_items
All connectable items in the schematic.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
std::shared_ptr< BUS_ALIAS > GetBusAlias(const wxString &aName)
Return a bus alias pointer for the given name if it exists (from cache)
void removeSubgraphs(std::set< CONNECTION_SUBGRAPH * > &aSubgraphs)
Remove references to the given subgraphs from all structures in the connection graph.
std::unordered_map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map
std::set< std::pair< SCH_SHEET_PATH, SCH_ITEM * > > ExtractAffectedItems(const std::set< SCH_ITEM * > &aItems)
For a set of items, this will remove the connected items and their associated data including subgraph...
wxString GetResolvedSubgraphName(const CONNECTION_SUBGRAPH *aSubGraph) const
Return the fully-resolved netname for a given subgraph.
bool ercCheckBusToBusEntryConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for conflicting bus entry to bus connections.
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
Cache of a subset of m_subgraphs.
void ExchangeItem(SCH_ITEM *aOldItem, SCH_ITEM *aNewItem)
Replace all references to #aOldItem with #aNewItem in the graph.
NET_MAP m_net_code_to_subgraphs_map
bool ercCheckFloatingWires(const CONNECTION_SUBGRAPH *aSubgraph)
Check one subgraph for floating wires.
void buildConnectionGraph(std::function< void(SCH_ITEM *)> *aChangedItemHandler, bool aUnconditional)
Generate the connection graph (after all item connectivity has been updated).
void Merge(CONNECTION_GRAPH &aGraph)
Combine the input graph contents into the current graph.
void updatePinConnectivity(const SCH_SHEET_PATH &aSheet, SCH_PIN *aPin, SCH_CONNECTION *aConnection)
Update the connectivity of a pin and its connections.
void resolveAllDrivers()
Find all subgraphs in the connection graph and calls ResolveDrivers() in parallel.
void updateItemConnectivity(const SCH_SHEET_PATH &aSheet, const std::vector< SCH_ITEM * > &aItemList)
Update the graphical connectivity between items (i.e.
CONNECTION_SUBGRAPH * GetSubgraphForItem(SCH_ITEM *aItem) const
void generateBusAliasMembers()
Iterate through labels to create placeholders for bus elements.
A subgraph is a set of items that are electrically connected on a single sheet.
wxString driverName(SCH_ITEM *aItem) const
bool m_strong_driver
True if the driver is "strong": a label or power object.
SCH_ITEM * m_no_connect
No-connect item in graph, if any.
std::set< CONNECTION_SUBGRAPH * > m_absorbed_subgraphs
Set of subgraphs that have been absorbed by this subgraph.
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Return the priority (higher is more important) of a candidate driver.
std::mutex m_driver_name_cache_mutex
A cache of escaped netnames from schematic items.
SCH_SHEET_PATH m_sheet
On which logical sheet is the subgraph contained.
void UpdateItemConnections()
Update all items to match the driver connection.
std::set< SCH_SHEET_PIN * > m_hier_pins
Cache for lookup of any hierarchical (sheet) pins on this subgraph (for referring down).
std::unordered_map< std::shared_ptr< SCH_CONNECTION >, std::unordered_set< CONNECTION_SUBGRAPH * > > m_bus_neighbors
If a subgraph is a bus, this map contains links between the bus members and any local sheet neighbors...
CONNECTION_GRAPH * m_graph
std::vector< SCH_ITEM * > GetAllBusLabels() const
Return all the all bus labels attached to this subgraph (if any).
std::unordered_map< SCH_ITEM *, wxString > m_driver_name_cache
const wxString & GetNameForDriver(SCH_ITEM *aItem) const
Return the candidate net name for a driver.
wxString GetNetName() const
Return the fully-qualified net name for this subgraph (if one exists)
std::vector< SCH_ITEM * > GetVectorBusLabels() const
Return all the vector-based bus labels attached to this subgraph (if any).
const SCH_SHEET_PATH & GetSheet() const
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
bool ResolveDrivers(bool aCheckMultipleDrivers=false)
Determine which potential driver should drive the subgraph.
std::set< SCH_ITEM * > m_drivers
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
std::unordered_set< CONNECTION_SUBGRAPH * > m_hier_children
If not null, this indicates the subgraph(s) on a lower level sheet that are linked to this one.
void AddItem(SCH_ITEM *aItem)
Add a new item to the subgraph.
const std::vector< std::pair< wxString, SCH_ITEM * > > GetNetclassesForDriver(SCH_ITEM *aItem) const
Return the resolved netclasses for the item, and the source item providing the netclass.
void Absorb(CONNECTION_SUBGRAPH *aOther)
Combine another subgraph on the same sheet into this one.
std::set< SCH_ITEM * > m_items
Contents of the subgraph.
std::unordered_map< std::shared_ptr< SCH_CONNECTION >, std::unordered_set< CONNECTION_SUBGRAPH * > > m_bus_parents
If this is a net, this vector contains links to any same-sheet buses that contain it.
SCH_ITEM * m_driver
Fully-resolved driver for the subgraph (might not exist in this subgraph).
CONNECTION_SUBGRAPH(CONNECTION_GRAPH *aGraph)
bool m_is_bus_member
True if the subgraph is not actually part of a net.
void ExchangeItem(SCH_ITEM *aOldItem, SCH_ITEM *aNewItem)
Replaces all references to #aOldItem with #aNewItem in the subgraph.
CONNECTION_SUBGRAPH * m_hier_parent
If not null, this indicates the subgraph on a higher level sheet that is linked to this one.
void RemoveItem(SCH_ITEM *aItem)
bool m_local_driver
True if the driver is a local (i.e. non-global) type.
std::set< SCH_HIERLABEL * > m_hier_ports
Cache for lookup of any hierarchical ports on this subgraph (for referring up).
void getAllConnectedItems(std::set< std::pair< SCH_SHEET_PATH, SCH_ITEM * > > &aItems, std::set< CONNECTION_SUBGRAPH * > &aSubgraphs)
Find all items in the subgraph as well as child subgraphs recursively.
virtual VECTOR2I GetPosition() const
Definition eda_item.h:278
virtual wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const
Return a user-visible description string of this item.
Definition eda_item.cpp:154
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:111
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:246
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:241
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition erc_item.cpp:312
Container for ERC settings.
bool IsTestEnabled(int aErrorCode) const
Definition kiid.h:49
bool GetDuplicatePinNumbersAreJumpers() const
Definition lib_symbol.h:753
std::vector< std::set< wxString > > & JumperPinGroups()
Each jumper pin group is a set of pin numbers that should be treated as internally connected.
Definition lib_symbol.h:760
A small class to help profiling.
Definition profile.h:49
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition profile.h:105
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:88
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
Class for a bus to bus entry.
SCH_ITEM * m_connected_bus_items[2]
Pointer to the bus items (usually bus wires) connected to this bus-bus entry (either or both may be n...
bool IsStartDangling() const
VECTOR2I GetPosition() const override
bool IsEndDangling() const
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Class for a wire to bus entry.
SCH_ITEM * m_connected_bus_item
Pointer to the bus item (usually a bus wire) connected to this bus-wire entry, if it is connected to ...
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
wxString FullLocalName() const
void ConfigureFromLabel(const wxString &aLabel)
Configures the connection given a label.
bool IsNet() const
void SetSubgraphCode(int aCode)
void SetBusCode(int aCode)
void SetName(const wxString &aName)
SCH_SHEET_PATH Sheet() const
CONNECTION_TYPE Type() const
int SubgraphCode() const
void SetNetCode(int aCode)
SCH_ITEM * m_driver
The SCH_ITEM that drives this connection's net.
bool IsDriver() const
Checks if the SCH_ITEM this connection is attached to can drive connections Drivers can be labels,...
void SetType(CONNECTION_TYPE aType)
wxString LocalName() const
wxString Name(bool aIgnoreSheet=false) const
bool IsSubsetOf(SCH_CONNECTION *aOther) const
Returns true if this connection is contained within aOther (but not the same as aOther)
void SetDriver(SCH_ITEM *aItem)
bool IsBus() const
void Clone(const SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
void SetGraph(CONNECTION_GRAPH *aGraph)
const std::vector< std::shared_ptr< SCH_CONNECTION > > & Members() const
long VectorIndex() const
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0, const wxString &aVariantName=wxEmptyString) const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
void ClearConnectedItems(const SCH_SHEET_PATH &aPath)
Clear all connections to this item.
Definition sch_item.cpp:535
virtual void RunOnChildren(const std::function< void(SCH_ITEM *)> &aFunction, RECURSE_MODE aMode)
Definition sch_item.h:631
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:260
virtual const wxString & GetCachedDriverName() const
Definition sch_item.cpp:605
const std::unordered_set< SCH_RULE_AREA * > & GetRuleAreaCache() const
Get the cache of rule areas enclosing this item.
Definition sch_item.h:684
SCH_CONNECTION * InitializeConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Create a new connection object associated with this object.
Definition sch_item.cpp:566
void AddConnectionTo(const SCH_SHEET_PATH &aPath, SCH_ITEM *aItem)
Add a connection link between this item and another.
Definition sch_item.cpp:550
int GetUnit() const
Definition sch_item.h:239
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:341
void SetConnectionGraph(CONNECTION_GRAPH *aGraph)
Update the connection graph for all connections in this item.
Definition sch_item.cpp:497
virtual void SetUnit(int aUnit)
Definition sch_item.h:238
virtual bool HasCachedDriverName() const
Definition sch_item.h:616
SCH_CONNECTION * GetOrInitConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Definition sch_item.cpp:590
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition sch_item.cpp:473
virtual std::vector< VECTOR2I > GetConnectionPoints() const
Add all the connection points for this item to aPoints.
Definition sch_item.h:542
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const override
bool IsDangling() const override
Definition sch_label.h:337
LABEL_FLAG_SHAPE GetShape() const
Definition sch_label.h:180
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Definition sch_line.cpp:717
bool IsStartDangling() const
Definition sch_line.h:303
bool IsEndDangling() const
Definition sch_line.h:304
bool IsGraphicLine() const
Return if the line is a graphic (non electrical line)
Definition sch_line.cpp:986
bool IsGlobalPower() const
Return whether this pin forms a global power connection: i.e., is part of a power symbol and of type ...
Definition sch_pin.cpp:359
bool IsLocalPower() const
Local power pin is the same except that it is sheet-local and it does not support the legacy hidden p...
Definition sch_pin.cpp:378
SCH_PIN * GetLibPin() const
Definition sch_pin.h:89
bool IsStacked(const SCH_PIN *aPin) const
Definition sch_pin.cpp:480
wxString GetDefaultNetName(const SCH_SHEET_PATH &aPath, bool aForceNoConnect=false)
Definition sch_pin.cpp:1431
bool IsPower() const
Check if the pin is either a global or local power pin.
Definition sch_pin.cpp:385
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:315
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void TestDanglingEnds(const SCH_SHEET_PATH *aPath=nullptr, std::function< void(SCH_ITEM *)> *aChangedHandler=nullptr) const
Test all of the connectable objects in the schematic for unused connection points.
std::vector< SCH_LINE * > GetBusesAndWires(const VECTOR2I &aPosition, bool aIgnoreEndpoints=false) const
Return buses and wires passing through aPosition.
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:119
SCH_LINE * GetBus(const VECTOR2I &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T) const
Definition sch_screen.h:451
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
const SCH_SHEET * GetSheet(unsigned aIndex) const
SCH_SCREEN * LastScreen()
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false, bool aEscapeSheetNames=false) const
Return the sheet path in a human readable form made from the sheet names.
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
size_t size() const
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
SCH_SHEET * GetParent() const
Get the parent sheet object of this sheet pin.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:371
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:140
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:228
Schematic symbol object.
Definition sch_symbol.h:76
bool IsInNetlist() const
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
SCH_PIN * GetPin(const wxString &number) const
Find a symbol pin by number.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:184
VECTOR2I GetPosition() const override
Definition sch_text.h:147
virtual wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
Definition sch_text.cpp:318
A base class for LIB_SYMBOL and SCH_SYMBOL.
Definition symbol.h:63
virtual bool IsGlobalPower() const =0
virtual bool IsLocalPower() const =0
virtual bool IsPower() const =0
The common library.
#define _(s)
@ NO_RECURSE
Definition eda_item.h:53
#define CONNECTIVITY_CANDIDATE
flag indicating that the structure is connected for connectivity
@ ERCE_DRIVER_CONFLICT
Conflicting drivers (labels, etc) on a subgraph.
@ ERCE_UNCONNECTED_WIRE_ENDPOINT
A label is connected to more than one wire.
@ ERCE_LABEL_NOT_CONNECTED
Label not connected to any pins.
@ ERCE_BUS_TO_BUS_CONFLICT
A connection between bus objects doesn't share at least one net.
@ ERCE_LABEL_SINGLE_PIN
A label is connected only to a single pin.
@ ERCE_BUS_ENTRY_CONFLICT
A wire connected to a bus doesn't match the bus.
@ ERCE_BUS_TO_NET_CONFLICT
A bus wire is graphically connected to a net port/pin (or vice versa).
@ ERCE_NOCONNECT_NOT_CONNECTED
A no connect symbol is not connected to anything.
@ ERCE_PIN_NOT_CONNECTED
Pin not connected and not no connect symbol.
@ ERCE_NOCONNECT_CONNECTED
A no connect symbol is connected to more than 1 pin.
@ ERCE_HIERACHICAL_LABEL
Mismatch between hierarchical labels and pins sheets.
@ ERCE_WIRE_DANGLING
Some wires are not connected to anything else.
@ ERCE_SINGLE_GLOBAL_LABEL
A label only exists once in the schematic.
static const wxChar DanglingProfileMask[]
Flag to enable connectivity profiling.
static const wxChar ConnTrace[]
Flag to enable connectivity tracing.
@ LAYER_WIRE
Definition layer_ids.h:452
@ LAYER_BUS
Definition layer_ids.h:453
@ LAYER_JUNCTION
Definition layer_ids.h:454
@ LAYER_BUS_JUNCTION
Definition layer_ids.h:498
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
void remove_duplicates(_Container &__c)
Deletes all duplicate values from __c.
Definition kicad_algo.h:161
@ PT_NC
not connected (must be left open)
Definition pin_type.h:50
@ PT_NIC
not internally connected (may be connected to anything)
Definition pin_type.h:44
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:46
CONNECTION_TYPE
@ BUS
This item represents a bus vector.
@ NET
This item represents a net.
@ BUS_GROUP
This item represents a bus group.
std::vector< SCH_ITEM * > SCH_ITEM_VEC
Definition sch_item.h:157
@ L_OUTPUT
Definition sch_label.h:101
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
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
std::string path
KIBIS_PIN * pin
VECTOR2I location
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:31
#define kv
@ SCH_LINE_T
Definition typeinfo.h:167
@ SCH_NO_CONNECT_T
Definition typeinfo.h:164
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_FIELD_T
Definition typeinfo.h:154
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:175
@ SCH_LABEL_T
Definition typeinfo.h:171
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_HIER_LABEL_T
Definition typeinfo.h:173
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:166
@ SCH_SHEET_PIN_T
Definition typeinfo.h:178
@ SCH_TEXT_T
Definition typeinfo.h:155
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:165
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:172
@ SCH_JUNCTION_T
Definition typeinfo.h:163
@ SCH_PIN_T
Definition typeinfo.h:157
Functions to provide common constants and other functions to assist in making a consistent UI.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695