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 }
1374
1375 for( auto& [point, connection_vec] : connection_map )
1376 {
1377 std::sort( connection_vec.begin(), connection_vec.end() );
1378 alg::remove_duplicates( connection_vec );
1379
1380 // Pre-scan to see if we have a bus at this location
1381 SCH_LINE* busLine = aSheet.LastScreen()->GetBus( point );
1382
1383 for( SCH_ITEM* connected_item : connection_vec )
1384 {
1385 // Bus entries are special: they can have connection points in the
1386 // middle of a wire segment, because the junction algo doesn't split
1387 // the segment in two where you place a bus entry. This means that
1388 // bus entries that don't land on the end of a line segment need to
1389 // have "virtual" connection points to the segments they graphically
1390 // touch.
1391 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1392 {
1393 // If this location only has the connection point of the bus
1394 // entry itself, this means that either the bus entry is not
1395 // connected to anything graphically, or that it is connected to
1396 // a segment at some point other than at one of the endpoints.
1397 if( connection_vec.size() == 1 )
1398 {
1399 if( busLine )
1400 {
1401 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1402 bus_entry->m_connected_bus_item = busLine;
1403 }
1404 }
1405 }
1406 // Bus-to-bus entries are treated just like bus wires
1407 else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
1408 {
1409 if( busLine )
1410 {
1411 auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
1412
1413 if( point == bus_entry->GetPosition() )
1414 bus_entry->m_connected_bus_items[0] = busLine;
1415 else
1416 bus_entry->m_connected_bus_items[1] = busLine;
1417
1418 bus_entry->AddConnectionTo( aSheet, busLine );
1419 busLine->AddConnectionTo( aSheet, bus_entry );
1420 continue;
1421 }
1422 }
1423 // Change junctions to be on bus junction layer if they are touching a bus
1424 else if( connected_item->Type() == SCH_JUNCTION_T )
1425 {
1426 connected_item->SetLayer( busLine ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
1427 }
1428
1429 for( SCH_ITEM* test_item : connection_vec )
1430 {
1431 bool bus_connection_ok = true;
1432
1433 if( test_item == connected_item )
1434 continue;
1435
1436 // Set up the link between the bus entry net and the bus
1437 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1438 {
1439 if( test_item->GetLayer() == LAYER_BUS )
1440 {
1441 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1442 bus_entry->m_connected_bus_item = test_item;
1443 }
1444 }
1445
1446 // Bus entries only connect to bus lines on the end that is touching a bus line.
1447 // If the user has overlapped another net line with the endpoint of the bus entry
1448 // where the entry connects to a bus, we don't want to short-circuit it.
1449 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1450 {
1451 bus_connection_ok = !busLine || test_item->GetLayer() == LAYER_BUS;
1452 }
1453 else if( test_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1454 {
1455 bus_connection_ok = !busLine || connected_item->GetLayer() == LAYER_BUS;
1456 }
1457
1458 if( connected_item->ConnectionPropagatesTo( test_item )
1459 && test_item->ConnectionPropagatesTo( connected_item )
1460 && bus_connection_ok )
1461 {
1462 connected_item->AddConnectionTo( aSheet, test_item );
1463 }
1464 }
1465
1466 // If we got this far and did not find a connected bus item for a bus entry,
1467 // we should do a manual scan in case there is a bus item on this connection
1468 // point but we didn't pick it up earlier because there is *also* a net item here.
1469 if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
1470 {
1471 auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
1472
1473 if( !bus_entry->m_connected_bus_item )
1474 {
1475 SCH_SCREEN* screen = aSheet.LastScreen();
1476 SCH_LINE* bus = screen->GetBus( point );
1477
1478 if( bus )
1479 bus_entry->m_connected_bus_item = bus;
1480 }
1481 }
1482 }
1483 }
1484}
1485
1486
1488{
1489 // Recache all bus aliases for later use
1490 wxCHECK_RET( m_schematic, wxS( "Connection graph cannot be built without schematic pointer" ) );
1491
1492 m_bus_alias_cache.clear();
1493
1494 for( const std::shared_ptr<BUS_ALIAS>& alias : m_schematic->GetAllBusAliases() )
1495 {
1496 if( alias )
1497 m_bus_alias_cache[alias->GetName()] = alias;
1498 }
1499
1500 // Build subgraphs from items (on a per-sheet basis)
1501 for( SCH_ITEM* item : m_items )
1502 {
1503 for( const auto& [sheet, connection] : item->m_connection_map )
1504 {
1505 if( connection->SubgraphCode() == 0 )
1506 {
1507 CONNECTION_SUBGRAPH* subgraph = new CONNECTION_SUBGRAPH( this );
1508
1509 subgraph->m_code = m_last_subgraph_code++;
1510 subgraph->m_sheet = sheet;
1511
1512 subgraph->AddItem( item );
1513
1514 connection->SetSubgraphCode( subgraph->m_code );
1515 m_item_to_subgraph_map[item] = subgraph;
1516
1517 std::list<SCH_ITEM*> memberlist;
1518
1519 auto get_items =
1520 [&]( SCH_ITEM* aItem ) -> bool
1521 {
1522 SCH_CONNECTION* conn = aItem->GetOrInitConnection( sheet, this );
1523 bool unique = !( aItem->GetFlags() & CONNECTIVITY_CANDIDATE );
1524
1525 if( conn && !conn->SubgraphCode() )
1526 aItem->SetFlags( CONNECTIVITY_CANDIDATE );
1527
1528 return ( unique && conn && ( conn->SubgraphCode() == 0 ) );
1529 };
1530
1531 std::copy_if( item->ConnectedItems( sheet ).begin(),
1532 item->ConnectedItems( sheet ).end(),
1533 std::back_inserter( memberlist ), get_items );
1534
1535 for( SCH_ITEM* connected_item : memberlist )
1536 {
1537 if( connected_item->Type() == SCH_NO_CONNECT_T )
1538 subgraph->m_no_connect = connected_item;
1539
1540 SCH_CONNECTION* connected_conn = connected_item->Connection( &sheet );
1541
1542 wxCHECK2( connected_conn, continue );
1543
1544 if( connected_conn->SubgraphCode() == 0 )
1545 {
1546 connected_conn->SetSubgraphCode( subgraph->m_code );
1547 m_item_to_subgraph_map[connected_item] = subgraph;
1548 subgraph->AddItem( connected_item );
1549 const SCH_ITEM_VEC& citemset = connected_item->ConnectedItems( sheet );
1550
1551 for( SCH_ITEM* citem : citemset )
1552 {
1553 if( citem->HasFlag( CONNECTIVITY_CANDIDATE ) )
1554 continue;
1555
1556 if( get_items( citem ) )
1557 memberlist.push_back( citem );
1558 }
1559 }
1560 }
1561
1562 for( SCH_ITEM* connected_item : memberlist )
1563 connected_item->ClearFlags( CONNECTIVITY_CANDIDATE );
1564
1565 subgraph->m_dirty = true;
1566 m_subgraphs.push_back( subgraph );
1567 }
1568 }
1569 }
1570}
1571
1572
1574{
1575 // Resolve drivers for subgraphs and propagate connectivity info
1576 std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
1577
1578 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
1579 [&] ( const CONNECTION_SUBGRAPH* candidate )
1580 {
1581 return candidate->m_dirty;
1582 } );
1583
1584 wxLogTrace( ConnTrace, wxT( "Resolving drivers for %zu subgraphs" ), dirty_graphs.size() );
1585
1586 std::vector<std::future<size_t>> returns( dirty_graphs.size() );
1587
1588 auto update_lambda =
1589 []( CONNECTION_SUBGRAPH* subgraph ) -> size_t
1590 {
1591 if( !subgraph->m_dirty )
1592 return 0;
1593
1594 // Special processing for some items
1595 for( SCH_ITEM* item : subgraph->m_items )
1596 {
1597 switch( item->Type() )
1598 {
1599 case SCH_NO_CONNECT_T:
1600 subgraph->m_no_connect = item;
1601 break;
1602
1604 subgraph->m_bus_entry = item;
1605 break;
1606
1607 case SCH_PIN_T:
1608 {
1609 auto pin = static_cast<SCH_PIN*>( item );
1610
1611 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
1612 subgraph->m_no_connect = item;
1613
1614 break;
1615 }
1616
1617 default:
1618 break;
1619 }
1620 }
1621
1622 subgraph->ResolveDrivers( true );
1623 subgraph->m_dirty = false;
1624
1625 return 1;
1626 };
1627
1629
1630 auto results = tp.submit_loop( 0, dirty_graphs.size(),
1631 [&]( const int ii )
1632 {
1633 update_lambda( dirty_graphs[ii] );
1634 } );
1635 results.wait();
1636
1637 // Now discard any non-driven subgraphs from further consideration
1638
1639 std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
1640 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1641 {
1642 return candidate->m_driver;
1643 } );
1644}
1645
1646
1648{
1649 // Check for subgraphs with the same net name but only weak drivers.
1650 // For example, two wires that are both connected to hierarchical
1651 // sheet pins that happen to have the same name, but are not the same.
1652
1653 for( auto&& subgraph : m_driver_subgraphs )
1654 {
1655 wxString full_name = subgraph->m_driver_connection->Name();
1656 wxString name = subgraph->m_driver_connection->Name( true );
1657 m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
1658
1659 // For vector buses, we need to cache the prefix also, as two different instances of the
1660 // weakly driven pin may have the same prefix but different vector start and end. We need
1661 // to treat those as needing renaming also, because otherwise if they end up on a sheet with
1662 // common usage, they will be incorrectly merged.
1663 if( subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1664 {
1665 wxString prefixOnly = full_name.BeforeFirst( '[' ) + wxT( "[]" );
1666 m_net_name_to_subgraphs_map[prefixOnly].emplace_back( subgraph );
1667 }
1668
1669 subgraph->m_dirty = true;
1670
1671 if( subgraph->m_strong_driver )
1672 {
1673 SCH_ITEM* driver = subgraph->m_driver;
1674 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1675
1676 switch( driver->Type() )
1677 {
1678 case SCH_LABEL_T:
1679 case SCH_HIER_LABEL_T:
1680 {
1681 m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
1682 break;
1683 }
1684 case SCH_GLOBAL_LABEL_T:
1685 {
1686 m_global_label_cache[name].push_back( subgraph );
1687 break;
1688 }
1689 case SCH_PIN_T:
1690 {
1691 SCH_PIN* pin = static_cast<SCH_PIN*>( driver );
1692 if( pin->IsGlobalPower() )
1693 {
1694 m_global_label_cache[name].push_back( subgraph );
1695 }
1696 else if( pin->IsLocalPower() )
1697 {
1698 m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
1699 }
1700 else
1701 {
1702 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
1703 wxLogTrace( ConnTrace, wxS( "Unexpected normal pin %s" ),
1704 driver->GetItemDescription( &unitsProvider, true ) );
1705 }
1706
1707 break;
1708 }
1709 default:
1710 {
1711 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
1712
1713 wxLogTrace( ConnTrace, wxS( "Unexpected strong driver %s" ),
1714 driver->GetItemDescription( &unitsProvider, true ) );
1715 break;
1716 }
1717 }
1718 }
1719 }
1720}
1721
1722
1724{
1725 std::vector<CONNECTION_SUBGRAPH*> new_subgraphs;
1726
1727 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1728 {
1729 SCH_ITEM_VEC vec = subgraph->GetAllBusLabels();
1730
1731 for( SCH_ITEM* item : vec )
1732 {
1733 SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( item );
1734
1735 SCH_CONNECTION dummy( item, subgraph->m_sheet );
1736 dummy.SetGraph( this );
1737 dummy.ConfigureFromLabel( label->GetShownText( &subgraph->m_sheet, false ) );
1738
1739 wxLogTrace( ConnTrace, wxS( "new bus label (%s)" ),
1740 label->GetShownText( &subgraph->m_sheet, false ) );
1741
1742 for( const auto& conn : dummy.Members() )
1743 {
1744 // Only create subgraphs for NET members, not nested buses
1745 if( !conn->IsNet() )
1746 continue;
1747
1748 wxString name = conn->FullLocalName();
1749
1750 CONNECTION_SUBGRAPH* new_sg = new CONNECTION_SUBGRAPH( this );
1751
1752 // This connection cannot form a part of the item because the item is not, itself
1753 // connected to this subgraph. It exists as part of a virtual item that may be
1754 // connected to other items but is not in the schematic.
1755 auto new_conn = std::make_unique<SCH_CONNECTION>( item, subgraph->m_sheet );
1756 new_conn->SetGraph( this );
1757 new_conn->SetName( name );
1758 new_conn->SetType( CONNECTION_TYPE::NET );
1759
1760 SCH_CONNECTION* new_conn_ptr = subgraph->StoreImplicitConnection( std::move( new_conn ) );
1761 int code = assignNewNetCode( *new_conn_ptr );
1762
1763 wxLogTrace( ConnTrace, wxS( "SG(%ld), Adding full local name (%s) with sg (%d) on subsheet %s" ),
1764 subgraph->m_code, name, code, subgraph->m_sheet.PathHumanReadable() );
1765
1766 new_sg->m_driver_connection = new_conn_ptr;
1767 new_sg->m_code = m_last_subgraph_code++;
1768 new_sg->m_sheet = subgraph->GetSheet();
1769 new_sg->m_is_bus_member = true;
1770 new_sg->m_strong_driver = true;
1771
1773 NET_NAME_CODE_CACHE_KEY key = { new_sg->GetNetName(), code };
1774 m_net_code_to_subgraphs_map[ key ].push_back( new_sg );
1775 m_net_name_to_subgraphs_map[ name ].push_back( new_sg );
1776 m_subgraphs.push_back( new_sg );
1777 new_subgraphs.push_back( new_sg );
1778 }
1779 }
1780 }
1781
1782 std::copy( new_subgraphs.begin(), new_subgraphs.end(),
1783 std::back_inserter( m_driver_subgraphs ) );
1784}
1785
1786
1788{
1789 // Generate subgraphs for global power pins. These will be merged with other subgraphs
1790 // on the same sheet in the next loop.
1791 // These are NOT limited to power symbols, we support legacy invisible + power-in pins
1792 // on non-power symbols.
1793
1794 // Sort power pins for deterministic processing order. This ensures that when multiple
1795 // power pins share the same net name, the same pin consistently creates the subgraph
1796 // across different ERC runs.
1797 std::sort( m_global_power_pins.begin(), m_global_power_pins.end(),
1798 []( const std::pair<SCH_SHEET_PATH, SCH_PIN*>& a,
1799 const std::pair<SCH_SHEET_PATH, SCH_PIN*>& b )
1800 {
1801 int pathCmp = a.first.Cmp( b.first );
1802
1803 if( pathCmp != 0 )
1804 return pathCmp < 0;
1805
1806 const SCH_SYMBOL* symA = static_cast<const SCH_SYMBOL*>( a.second->GetParentSymbol() );
1807 const SCH_SYMBOL* symB = static_cast<const SCH_SYMBOL*>( b.second->GetParentSymbol() );
1808
1809 wxString refA = symA ? symA->GetRef( &a.first, false ) : wxString();
1810 wxString refB = symB ? symB->GetRef( &b.first, false ) : wxString();
1811
1812 int refCmp = refA.Cmp( refB );
1813
1814 if( refCmp != 0 )
1815 return refCmp < 0;
1816
1817 return a.second->GetNumber().Cmp( b.second->GetNumber() ) < 0;
1818 } );
1819
1820 std::unordered_map<int, CONNECTION_SUBGRAPH*> global_power_pin_subgraphs;
1821
1822 for( const auto& [sheet, pin] : m_global_power_pins )
1823 {
1824 SYMBOL* libParent = pin->GetLibPin() ? pin->GetLibPin()->GetParentSymbol() : nullptr;
1825
1826 if( !pin->ConnectedItems( sheet ).empty()
1827 && ( !libParent || !libParent->IsGlobalPower() ) )
1828 {
1829 // ERC will warn about this: user has wired up an invisible pin
1830 continue;
1831 }
1832
1833 SCH_CONNECTION* connection = pin->GetOrInitConnection( sheet, this );
1834
1835 // If this pin already has a subgraph, don't need to process
1836 if( !connection || connection->SubgraphCode() > 0 )
1837 continue;
1838
1839 // Proper modern power symbols get their net name from the value field
1840 // in the symbol, but we support legacy non-power symbols with global
1841 // power connections based on invisible, power-in, pin's names.
1842 if( libParent && libParent->IsGlobalPower() )
1843 connection->SetName( pin->GetParentSymbol()->GetValue( true, &sheet, false ) );
1844 else
1845 connection->SetName( pin->GetShownName() );
1846
1847 int code = assignNewNetCode( *connection );
1848
1849 connection->SetNetCode( code );
1850
1851 CONNECTION_SUBGRAPH* subgraph;
1852 auto jj = global_power_pin_subgraphs.find( code );
1853
1854 if( jj != global_power_pin_subgraphs.end() )
1855 {
1856 subgraph = jj->second;
1857 subgraph->AddItem( pin );
1858 }
1859 else
1860 {
1861 subgraph = new CONNECTION_SUBGRAPH( this );
1862
1863 subgraph->m_code = m_last_subgraph_code++;
1864 subgraph->m_sheet = sheet;
1865
1866 subgraph->AddItem( pin );
1867 subgraph->ResolveDrivers();
1868
1869 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(), code };
1870 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1871 m_subgraphs.push_back( subgraph );
1872 m_driver_subgraphs.push_back( subgraph );
1873
1874 global_power_pin_subgraphs[code] = subgraph;
1875 }
1876
1877 connection->SetSubgraphCode( subgraph->m_code );
1878 }
1879}
1880
1881
1883{
1884 // Here we do all the local (sheet) processing of each subgraph, including assigning net
1885 // codes, merging subgraphs together that use label connections, etc.
1886
1887 // Cache remaining valid subgraphs by sheet path
1888 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1889 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1890
1891 std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
1892
1893 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1894 {
1895 if( subgraph->m_absorbed )
1896 continue;
1897
1898 SCH_CONNECTION* connection = subgraph->m_driver_connection;
1899 SCH_SHEET_PATH sheet = subgraph->m_sheet;
1900 wxString name = connection->Name();
1901
1902 // Test subgraphs with weak drivers for net name conflicts and fix them
1903 unsigned suffix = 1;
1904
1905 auto create_new_name =
1906 [&suffix]( SCH_CONNECTION* aConn ) -> wxString
1907 {
1908 wxString newName;
1909 wxString suffixStr = std::to_wstring( suffix );
1910
1911 // For group buses with a prefix, we can add the suffix to the prefix.
1912 // If they don't have a prefix, we force the creation of a prefix so that
1913 // two buses don't get inadvertently shorted together.
1914 if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
1915 {
1916 wxString prefix = aConn->BusPrefix();
1917
1918 if( prefix.empty() )
1919 prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
1920
1921 wxString oldName = aConn->Name().AfterFirst( '{' );
1922
1923 newName << prefix << wxT( "_" ) << suffixStr << wxT( "{" ) << oldName;
1924
1925 aConn->ConfigureFromLabel( newName );
1926 }
1927 else
1928 {
1929 newName << aConn->Name() << wxT( "_" ) << suffixStr;
1930 aConn->SetSuffix( wxString( wxT( "_" ) ) << suffixStr );
1931 }
1932
1933 suffix++;
1934 return newName;
1935 };
1936
1937 if( !subgraph->m_strong_driver )
1938 {
1939 std::vector<CONNECTION_SUBGRAPH*> vec_empty;
1940 std::vector<CONNECTION_SUBGRAPH*>* vec = &vec_empty;
1941
1942 if( m_net_name_to_subgraphs_map.count( name ) )
1943 vec = &m_net_name_to_subgraphs_map.at( name );
1944
1945 // If we are a unique bus vector, check if we aren't actually unique because of another
1946 // subgraph with a similar bus vector
1947 if( vec->size() <= 1 && subgraph->m_driver_connection->Type() == CONNECTION_TYPE::BUS )
1948 {
1949 wxString prefixOnly = name.BeforeFirst( '[' ) + wxT( "[]" );
1950
1951 if( m_net_name_to_subgraphs_map.count( prefixOnly ) )
1952 vec = &m_net_name_to_subgraphs_map.at( prefixOnly );
1953 }
1954
1955 if( vec->size() > 1 )
1956 {
1957 wxString new_name = create_new_name( connection );
1958
1959 while( m_net_name_to_subgraphs_map.contains( new_name ) )
1960 new_name = create_new_name( connection );
1961
1962 wxLogTrace( ConnTrace, wxS( "%ld (%s) is weakly driven and not unique. Changing to %s." ),
1963 subgraph->m_code, name, new_name );
1964
1965 std::erase( *vec, subgraph );
1966
1967 m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1968
1969 name = new_name;
1970 }
1971 else if( subgraph->m_driver )
1972 {
1973 // If there is no conflict, promote sheet pins to be strong drivers so that they
1974 // will be considered below for propagation/merging.
1975
1976 // It is possible for this to generate a conflict if the sheet pin has the same
1977 // name as a global label on the same sheet, because global merging will then treat
1978 // this subgraph as if it had a matching local label. So, for those cases, we
1979 // don't apply this promotion
1980
1981 if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1982 {
1983 bool conflict = false;
1984 wxString global_name = connection->Name( true );
1985 auto kk = m_net_name_to_subgraphs_map.find( global_name );
1986
1987 if( kk != m_net_name_to_subgraphs_map.end() )
1988 {
1989 // A global will conflict if it is on the same sheet as this subgraph, since
1990 // it would be connected by implicit local label linking
1991 std::vector<CONNECTION_SUBGRAPH*>& candidates = kk->second;
1992
1993 for( const CONNECTION_SUBGRAPH* candidate : candidates )
1994 {
1995 if( candidate->m_sheet == sheet )
1996 conflict = true;
1997 }
1998 }
1999
2000 if( conflict )
2001 {
2002 wxLogTrace( ConnTrace, wxS( "%ld (%s) skipped for promotion due to potential conflict" ),
2003 subgraph->m_code, name );
2004 }
2005 else
2006 {
2007 UNITS_PROVIDER unitsProvider( schIUScale, EDA_UNITS::MM );
2008
2009 wxLogTrace( ConnTrace, wxS( "%ld (%s) weakly driven by unique sheet pin %s, promoting" ),
2010 subgraph->m_code, name,
2011 subgraph->m_driver->GetItemDescription( &unitsProvider, true ) );
2012
2013 subgraph->m_strong_driver = true;
2014 }
2015 }
2016 }
2017 }
2018
2019 // Assign net codes
2020 if( connection->IsBus() )
2021 {
2022 int code = -1;
2023 auto it = m_bus_name_to_code_map.find( name );
2024
2025 if( it != m_bus_name_to_code_map.end() )
2026 {
2027 code = it->second;
2028 }
2029 else
2030 {
2031 code = m_last_bus_code++;
2032 m_bus_name_to_code_map[ name ] = code;
2033 }
2034
2035 connection->SetBusCode( code );
2036 assignNetCodesToBus( connection );
2037 }
2038 else
2039 {
2040 assignNewNetCode( *connection );
2041 }
2042
2043 // Reset the flag for the next loop below
2044 subgraph->m_dirty = true;
2045
2046 // Next, we merge together subgraphs that have label connections, and create
2047 // neighbor links for subgraphs that are part of a bus on the same sheet.
2048 // For merging, we consider each possible strong driver.
2049
2050 // If this subgraph doesn't have a strong driver, let's skip it, since there is no
2051 // way it will be merged with anything.
2052 if( !subgraph->m_strong_driver )
2053 continue;
2054
2055 // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
2056 // as the subgraph we are considering that has a strong driver.
2057 // Weakly driven subgraphs are not considered since they will never be absorbed or
2058 // form neighbor links.
2059 std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
2060 std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
2061 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
2062 std::back_inserter( candidate_subgraphs ),
2063 [&] ( const CONNECTION_SUBGRAPH* candidate )
2064 {
2065 return ( !candidate->m_absorbed &&
2066 candidate->m_strong_driver &&
2067 candidate != subgraph );
2068 } );
2069
2070 // This is a list of connections on the current subgraph to compare to the
2071 // drivers of each candidate subgraph. If the current subgraph is a bus,
2072 // we should consider each bus member.
2073 std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
2074
2075 // Also check the main driving connection
2076 connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
2077
2078 auto add_connections_to_check =
2079 [&] ( CONNECTION_SUBGRAPH* aSubgraph )
2080 {
2081 for( SCH_ITEM* possible_driver : aSubgraph->m_items )
2082 {
2083 if( possible_driver == aSubgraph->m_driver )
2084 continue;
2085
2086 auto c = getDefaultConnection( possible_driver, aSubgraph );
2087
2088 if( c )
2089 {
2090 if( c->Type() != aSubgraph->m_driver_connection->Type() )
2091 continue;
2092
2093 if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
2094 continue;
2095
2096 connections_to_check.push_back( c );
2097 wxLogTrace( ConnTrace, wxS( "%lu (%s): Adding secondary driver %s" ),
2098 aSubgraph->m_code,
2099 aSubgraph->m_driver_connection->Name( true ),
2100 c->Name( true ) );
2101 }
2102 }
2103 };
2104
2105 // Now add other strong drivers
2106 // The actual connection attached to these items will have been overwritten
2107 // by the chosen driver of the subgraph, so we need to create a dummy connection
2108 add_connections_to_check( subgraph );
2109
2110 std::set<SCH_CONNECTION*> checked_connections;
2111
2112 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2113 {
2114 auto member = connections_to_check[i];
2115
2116 // Don't check the same connection twice
2117 if( !checked_connections.insert( member.get() ).second )
2118 continue;
2119
2120 if( member->IsBus() )
2121 {
2122 connections_to_check.insert( connections_to_check.end(),
2123 member->Members().begin(),
2124 member->Members().end() );
2125 }
2126
2127 wxString test_name = member->Name( true );
2128
2129 for( CONNECTION_SUBGRAPH* candidate : candidate_subgraphs )
2130 {
2131 if( candidate->m_absorbed || candidate == subgraph )
2132 continue;
2133
2134 bool match = false;
2135
2136 if( candidate->m_driver_connection->Name( true ) == test_name )
2137 {
2138 match = true;
2139 }
2140 else
2141 {
2142 if( !candidate->m_multiple_drivers )
2143 continue;
2144
2145 for( SCH_ITEM *driver : candidate->m_drivers )
2146 {
2147 if( driver == candidate->m_driver )
2148 continue;
2149
2150 // Sheet pins are not candidates for merging
2151 if( driver->Type() == SCH_SHEET_PIN_T )
2152 continue;
2153
2154 if( driver->Type() == SCH_PIN_T )
2155 {
2156 auto pin = static_cast<SCH_PIN*>( driver );
2157
2158 if( pin->IsPower()
2159 && pin->GetDefaultNetName( sheet ) == test_name )
2160 {
2161 match = true;
2162 break;
2163 }
2164 }
2165 else
2166 {
2167 // Should we skip this if the driver type is not one of these types?
2168 wxASSERT( driver->Type() == SCH_LABEL_T ||
2169 driver->Type() == SCH_GLOBAL_LABEL_T ||
2170 driver->Type() == SCH_HIER_LABEL_T );
2171
2172 if( subgraph->GetNameForDriver( driver ) == test_name )
2173 {
2174 match = true;
2175 break;
2176 }
2177 }
2178 }
2179 }
2180
2181 if( match )
2182 {
2183 if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
2184 {
2185 wxLogTrace( ConnTrace, wxS( "%lu (%s) has bus child %lu (%s)" ),
2186 subgraph->m_code, connection->Name(),
2187 candidate->m_code, member->Name() );
2188
2189 subgraph->m_bus_neighbors[member].insert( candidate );
2190 candidate->m_bus_parents[member].insert( subgraph );
2191 }
2192 else if( !connection->IsBus()
2193 || connection->Type() == candidate->m_driver_connection->Type() )
2194 {
2195 wxLogTrace( ConnTrace, wxS( "%lu (%s) absorbs neighbor %lu (%s)" ),
2196 subgraph->m_code, connection->Name(),
2197 candidate->m_code, candidate->m_driver_connection->Name() );
2198
2199 // Candidate may have other non-chosen drivers we need to follow
2200 add_connections_to_check( candidate );
2201
2202 subgraph->Absorb( candidate );
2203 invalidated_subgraphs.insert( subgraph );
2204 }
2205 }
2206 }
2207 }
2208 }
2209
2210 // Update any subgraph that was invalidated above
2211 for( CONNECTION_SUBGRAPH* subgraph : invalidated_subgraphs )
2212 {
2213 if( subgraph->m_absorbed )
2214 continue;
2215
2216 if( !subgraph->ResolveDrivers() )
2217 continue;
2218
2219 if( subgraph->m_driver_connection->IsBus() )
2220 assignNetCodesToBus( subgraph->m_driver_connection );
2221 else
2222 assignNewNetCode( *subgraph->m_driver_connection );
2223
2224 wxLogTrace( ConnTrace, wxS( "Re-resolving drivers for %lu (%s)" ),
2225 subgraph->m_code, subgraph->m_driver_connection->Name() );
2226 }
2227
2228}
2229
2230
2231// TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
2232// to the same subgraph necessarily if it runs over and over again on the same
2233// sheet. We need:
2234//
2235// a) a cache of net/bus codes, like used before
2236// b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
2237// c) some way of trying to avoid changing net names. so we should keep track
2238// of the previous driver of a net, and if it comes down to choosing between
2239// equally-prioritized drivers, choose the one that already exists as a driver
2240// on some portion of the items.
2241
2242
2243void CONNECTION_GRAPH::buildConnectionGraph( std::function<void( SCH_ITEM* )>* aChangedItemHandler,
2244 bool aUnconditional )
2245{
2246 // Recache all bus aliases for later use
2247 wxCHECK_RET( m_schematic, wxT( "Connection graph cannot be built without schematic pointer" ) );
2248
2249 m_bus_alias_cache.clear();
2250
2251 for( const std::shared_ptr<BUS_ALIAS>& alias : m_schematic->GetAllBusAliases() )
2252 {
2253 if( alias )
2254 m_bus_alias_cache[alias->GetName()] = alias;
2255 }
2256
2257 PROF_TIMER sub_graph( "buildItemSubGraphs" );
2259
2260 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
2261 sub_graph.Show();
2262
2267
2269
2271
2273
2275
2276 PROF_TIMER proc_sub_graph( "ProcessSubGraphs" );
2278
2279 if( wxLog::IsAllowedTraceMask( DanglingProfileMask ) )
2280 proc_sub_graph.Show();
2281
2282 // Absorbed subgraphs should no longer be considered
2283 std::erase_if( m_driver_subgraphs, [&]( const CONNECTION_SUBGRAPH* candidate ) -> bool
2284 {
2285 return candidate->m_absorbed;
2286 } );
2287
2288 // Store global subgraphs for later reference
2289 std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
2290 std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
2291 std::back_inserter( global_subgraphs ),
2292 [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
2293 {
2294 return !candidate->m_local_driver;
2295 } );
2296
2297 // Recache remaining valid subgraphs by sheet path
2299
2300 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2301 m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
2302
2304
2305 auto results = tp.submit_loop( 0, m_driver_subgraphs.size(),
2306 [&]( const int ii )
2307 {
2308 m_driver_subgraphs[ii]->UpdateItemConnections();
2309 });
2310
2311 results.wait();
2312
2313 // Next time through the subgraphs, we do some post-processing to handle things like
2314 // connecting bus members to their neighboring subgraphs, and then propagate connections
2315 // through the hierarchy
2316 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2317 {
2318 if( !subgraph->m_dirty )
2319 continue;
2320
2321 wxLogTrace( ConnTrace, wxS( "Processing %lu (%s) for propagation" ),
2322 subgraph->m_code, subgraph->m_driver_connection->Name() );
2323
2324 // For subgraphs that are driven by a global (power port or label) and have more
2325 // than one global driver, we need to seek out other subgraphs driven by the
2326 // same name as the non-chosen driver and update them to match the chosen one.
2327
2328 if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
2329 {
2330 for( SCH_ITEM* driver : subgraph->m_drivers )
2331 {
2332 if( driver == subgraph->m_driver )
2333 continue;
2334
2335 const wxString& secondary_name = subgraph->GetNameForDriver( driver );
2336
2337 if( secondary_name == subgraph->m_driver_connection->Name() )
2338 continue;
2339
2340 bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
2342
2343 for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
2344 {
2345 if( candidate == subgraph )
2346 continue;
2347
2348 if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
2349 continue;
2350
2351 for( SCH_ITEM* candidate_driver : candidate->m_drivers )
2352 {
2353 if( candidate->GetNameForDriver( candidate_driver ) == secondary_name )
2354 {
2355 wxLogTrace( ConnTrace, wxS( "Global %lu (%s) promoted to %s" ),
2356 candidate->m_code, candidate->m_driver_connection->Name(),
2357 subgraph->m_driver_connection->Name() );
2358
2359 candidate->m_driver_connection->Clone( *subgraph->m_driver_connection );
2360
2361 candidate->m_dirty = false;
2362 propagateToNeighbors( candidate, false );
2363 }
2364 }
2365 }
2366 }
2367 }
2368
2369 // This call will handle descending the hierarchy and updating child subgraphs
2370 propagateToNeighbors( subgraph, false );
2371 }
2372
2373 // After processing and allowing some to be skipped if they have hierarchical
2374 // pins connecting both up and down the hierarchy, we check to see if any of them
2375 // have not been processed. This would indicate that they do not have off-sheet connections
2376 // but we still need to handle the subgraph
2377 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2378 {
2379 if( subgraph->m_dirty )
2380 propagateToNeighbors( subgraph, true );
2381 }
2382
2383 // Handle buses that have been linked together somewhere by member (net) connections.
2384 // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
2385
2386 // For net subgraphs that have more than one bus parent, we need to ensure that those
2387 // buses are linked together in the final netlist. The final name of each bus might not
2388 // match the local name that was used to establish the parent-child relationship, because
2389 // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
2390 // we need to identify the appropriate bus members to link together (and their final names),
2391 // and then update all instances of the old name in the hierarchy.
2392 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2393 {
2394 // All SGs should have been processed by propagateToNeighbors above
2395 // Should we skip all of this if the subgraph is not dirty?
2396 wxASSERT_MSG( !subgraph->m_dirty,
2397 wxS( "Subgraph not processed by propagateToNeighbors!" ) );
2398
2399 if( subgraph->m_bus_parents.size() < 2 )
2400 continue;
2401
2402 SCH_CONNECTION* conn = subgraph->m_driver_connection;
2403
2404 wxLogTrace( ConnTrace, wxS( "%lu (%s) has multiple bus parents" ),
2405 subgraph->m_code, conn->Name() );
2406
2407 // Should we skip everything after this if this is not a net?
2408 wxCHECK2( conn->IsNet(), continue );
2409
2410 for( const auto& ii : subgraph->m_bus_parents )
2411 {
2412 SCH_CONNECTION* link_member = ii.first.get();
2413
2414 for( CONNECTION_SUBGRAPH* parent : ii.second )
2415 {
2416 while( parent->m_absorbed )
2417 parent = parent->m_absorbed_by;
2418
2419 SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
2420
2421 if( !match )
2422 {
2423 wxLogTrace( ConnTrace, wxS( "Warning: could not match %s inside %lu (%s)" ),
2424 conn->Name(), parent->m_code, parent->m_driver_connection->Name() );
2425 continue;
2426 }
2427
2428 if( conn->Name() != match->Name() )
2429 {
2430 wxString old_name = match->Name();
2431
2432 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ),
2433 parent->m_code, parent->m_driver_connection->Name(), old_name, conn->Name() );
2434
2435 match->Clone( *conn );
2436
2437 auto jj = m_net_name_to_subgraphs_map.find( old_name );
2438
2439 if( jj == m_net_name_to_subgraphs_map.end() )
2440 continue;
2441
2442 // Copy the vector to avoid iterator invalidation when recaching
2443 std::vector<CONNECTION_SUBGRAPH*> old_subgraphs = jj->second;
2444
2445 for( CONNECTION_SUBGRAPH* old_sg : old_subgraphs )
2446 {
2447 while( old_sg->m_absorbed )
2448 old_sg = old_sg->m_absorbed_by;
2449
2450 wxString old_sg_name = old_sg->m_driver_connection->Name();
2451 old_sg->m_driver_connection->Clone( *conn );
2452
2453 if( old_sg_name != old_sg->m_driver_connection->Name() )
2454 recacheSubgraphName( old_sg, old_sg_name );
2455 }
2456 }
2457 }
2458 }
2459 }
2460
2461 auto updateItemConnectionsTask =
2462 [&]( CONNECTION_SUBGRAPH* subgraph ) -> size_t
2463 {
2464 // Make sure weakly-driven single-pin nets get the unconnected_ prefix
2465 if( !subgraph->m_strong_driver
2466 && subgraph->m_drivers.size() == 1
2467 && subgraph->m_driver->Type() == SCH_PIN_T )
2468 {
2469 SCH_PIN* pin = static_cast<SCH_PIN*>( subgraph->m_driver );
2470 wxString name = pin->GetDefaultNetName( subgraph->m_sheet, true );
2471
2472 subgraph->m_driver_connection->ConfigureFromLabel( name );
2473 }
2474
2475 subgraph->m_dirty = false;
2476 subgraph->UpdateItemConnections();
2477
2478 // No other processing to do on buses
2479 if( subgraph->m_driver_connection->IsBus() )
2480 return 0;
2481
2482 // As a visual aid, we can check sheet pins that are driven by themselves to see
2483 // if they should be promoted to buses
2484 if( subgraph->m_driver && subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
2485 {
2486 SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
2487
2488 if( SCH_SHEET* sheet = pin->GetParent() )
2489 {
2490 wxString pinText = pin->GetShownText( false );
2491 SCH_SCREEN* screen = sheet->GetScreen();
2492
2493 for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
2494 {
2495 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( item );
2496
2497 if( label->GetShownText( &subgraph->m_sheet, false ) == pinText )
2498 {
2499 SCH_SHEET_PATH path = subgraph->m_sheet;
2500 path.push_back( sheet );
2501
2502 SCH_CONNECTION* parent_conn = label->Connection( &path );
2503
2504 if( parent_conn && parent_conn->IsBus() )
2505 subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
2506
2507 break;
2508 }
2509 }
2510
2511 if( subgraph->m_driver_connection->IsBus() )
2512 return 0;
2513 }
2514 }
2515
2516 return 1;
2517 };
2518
2519 auto results2 = tp.submit_loop( 0, m_driver_subgraphs.size(),
2520 [&]( const int ii )
2521 {
2522 updateItemConnectionsTask( m_driver_subgraphs[ii] );
2523 } );
2524 results2.wait();
2525
2528
2529 for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
2530 {
2531 NET_NAME_CODE_CACHE_KEY key = { subgraph->GetNetName(),
2532 subgraph->m_driver_connection->NetCode() };
2533 m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
2534
2535 m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
2536 }
2537
2538 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Project().GetProjectFile().m_NetSettings;
2539 std::map<wxString, std::set<wxString>> oldAssignments = netSettings->GetNetclassLabelAssignments();
2540 std::set<wxString> affectedNetclassNetAssignments;
2541
2542 netSettings->ClearNetclassLabelAssignments();
2543
2544 auto dirtySubgraphs =
2545 [&]( const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2546 {
2547 if( aChangedItemHandler )
2548 {
2549 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2550 {
2551 for( SCH_ITEM* item : subgraph->m_items )
2552 (*aChangedItemHandler)( item );
2553 }
2554 }
2555 };
2556
2557 auto checkNetclassDrivers =
2558 [&]( const wxString& netName, const std::vector<CONNECTION_SUBGRAPH*>& subgraphs )
2559 {
2560 wxCHECK_RET( !subgraphs.empty(), wxS( "Invalid empty subgraph" ) );
2561
2562 std::set<wxString> netclasses;
2563
2564 // Collect all netclasses on all subgraphs for this net
2565 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2566 {
2567 for( SCH_ITEM* item : subgraph->m_items )
2568 {
2569 for( const auto& [name, provider] : subgraph->GetNetclassesForDriver( item ) )
2570 netclasses.insert( name );
2571 }
2572 }
2573
2574 // Append the netclasses to any included bus members
2575 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
2576 {
2577 if( subgraph->m_driver_connection->IsBus() )
2578 {
2579 auto processBusMember = [&, this]( const SCH_CONNECTION* member )
2580 {
2581 if( !netclasses.empty() )
2582 {
2583 netSettings->AppendNetclassLabelAssignment( member->Name(), netclasses );
2584 }
2585
2586 auto ii = m_net_name_to_subgraphs_map.find( member->Name() );
2587
2588 if( oldAssignments.count( member->Name() ) )
2589 {
2590 if( oldAssignments[member->Name()] != netclasses )
2591 {
2592 affectedNetclassNetAssignments.insert( member->Name() );
2593
2594 if( ii != m_net_name_to_subgraphs_map.end() )
2595 dirtySubgraphs( ii->second );
2596 }
2597 }
2598 else if( !netclasses.empty() )
2599 {
2600 affectedNetclassNetAssignments.insert( member->Name() );
2601
2602 if( ii != m_net_name_to_subgraphs_map.end() )
2603 dirtySubgraphs( ii->second );
2604 }
2605 };
2606
2607 for( const std::shared_ptr<SCH_CONNECTION>& member : subgraph->m_driver_connection->Members() )
2608 {
2609 // Check if this member itself is a bus (which can be the case for vector buses as members
2610 // of a bus, see https://gitlab.com/kicad/code/kicad/-/issues/16545
2611 if( member->IsBus() )
2612 {
2613 for( const std::shared_ptr<SCH_CONNECTION>& nestedMember : member->Members() )
2614 processBusMember( nestedMember.get() );
2615 }
2616 else
2617 {
2618 processBusMember( member.get() );
2619 }
2620 }
2621 }
2622 }
2623
2624 // Assign the netclasses to the root netname
2625 if( !netclasses.empty() )
2626 {
2627 netSettings->AppendNetclassLabelAssignment( netName, netclasses );
2628 }
2629
2630 if( oldAssignments.count( netName ) )
2631 {
2632 if( oldAssignments[netName] != netclasses )
2633 {
2634 affectedNetclassNetAssignments.insert( netName );
2635 dirtySubgraphs( subgraphs );
2636 }
2637 }
2638 else if( !netclasses.empty() )
2639 {
2640 affectedNetclassNetAssignments.insert( netName );
2641 dirtySubgraphs( subgraphs );
2642 }
2643 };
2644
2645 // Check for netclass assignments
2646 for( const auto& [ netname, subgraphs ] : m_net_name_to_subgraphs_map )
2647 checkNetclassDrivers( netname, subgraphs );
2648
2649 if( !aUnconditional )
2650 {
2651 for( auto& [netname, netclasses] : oldAssignments )
2652 {
2653 if( netSettings->GetNetclassLabelAssignments().count( netname )
2654 || affectedNetclassNetAssignments.count( netname ) )
2655 {
2656 continue;
2657 }
2658
2659 netSettings->SetNetclassLabelAssignment( netname, netclasses );
2660 }
2661 }
2662}
2663
2664
2665int CONNECTION_GRAPH::getOrCreateNetCode( const wxString& aNetName )
2666{
2667 int code;
2668
2669 auto it = m_net_name_to_code_map.find( aNetName );
2670
2671 if( it == m_net_name_to_code_map.end() )
2672 {
2673 code = m_last_net_code++;
2674 m_net_name_to_code_map[ aNetName ] = code;
2675 }
2676 else
2677 {
2678 code = it->second;
2679 }
2680
2681 return code;
2682}
2683
2684
2686{
2687 int code = getOrCreateNetCode( aConnection.Name() );
2688
2689 aConnection.SetNetCode( code );
2690
2691 return code;
2692}
2693
2694
2696{
2697 std::vector<std::shared_ptr<SCH_CONNECTION>> connections_to_check( aConnection->Members() );
2698
2699 for( unsigned i = 0; i < connections_to_check.size(); i++ )
2700 {
2701 const std::shared_ptr<SCH_CONNECTION>& member = connections_to_check[i];
2702
2703 if( member->IsBus() )
2704 {
2705 connections_to_check.insert( connections_to_check.end(),
2706 member->Members().begin(),
2707 member->Members().end() );
2708 continue;
2709 }
2710
2711 assignNewNetCode( *member );
2712 }
2713}
2714
2715
2717{
2718 SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
2719 std::vector<CONNECTION_SUBGRAPH*> search_list;
2720 std::unordered_set<CONNECTION_SUBGRAPH*> visited;
2721 std::unordered_set<SCH_CONNECTION*> stale_bus_members;
2722
2723 auto visit =[&]( CONNECTION_SUBGRAPH* aParent )
2724 {
2725 for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
2726 {
2727 SCH_SHEET_PATH path = aParent->m_sheet;
2728 path.push_back( pin->GetParent() );
2729
2730 auto it = m_sheet_to_subgraphs_map.find( path );
2731
2732 if( it == m_sheet_to_subgraphs_map.end() )
2733 continue;
2734
2735 for( CONNECTION_SUBGRAPH* candidate : it->second )
2736 {
2737 if( !candidate->m_strong_driver
2738 || candidate->m_hier_ports.empty()
2739 || visited.contains( candidate ) )
2740 {
2741 continue;
2742 }
2743
2744 for( SCH_HIERLABEL* label : candidate->m_hier_ports )
2745 {
2746 if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
2747 {
2748 wxLogTrace( ConnTrace, wxS( "%lu: found child %lu (%s)" ), aParent->m_code,
2749 candidate->m_code, candidate->m_driver_connection->Name() );
2750
2751 candidate->m_hier_parent = aParent;
2752 aParent->m_hier_children.insert( candidate );
2753
2754 // Should we skip adding the candidate to the list if the parent and candidate subgraphs
2755 // are not the same?
2756 wxASSERT( candidate->m_graph == aParent->m_graph );
2757
2758 search_list.push_back( candidate );
2759 break;
2760 }
2761 }
2762 }
2763 }
2764
2765 for( SCH_HIERLABEL* label : aParent->m_hier_ports )
2766 {
2767 SCH_SHEET_PATH path = aParent->m_sheet;
2768 path.pop_back();
2769
2770 auto it = m_sheet_to_subgraphs_map.find( path );
2771
2772 if( it == m_sheet_to_subgraphs_map.end() )
2773 continue;
2774
2775 for( CONNECTION_SUBGRAPH* candidate : it->second )
2776 {
2777 if( candidate->m_hier_pins.empty()
2778 || visited.contains( candidate )
2779 || candidate->m_driver_connection->Type() != aParent->m_driver_connection->Type() )
2780 {
2781 continue;
2782 }
2783
2784 const KIID& last_parent_uuid = aParent->m_sheet.Last()->m_Uuid;
2785
2786 for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
2787 {
2788 // If the last sheet UUIDs won't match, no need to check the full path
2789 if( pin->GetParent()->m_Uuid != last_parent_uuid )
2790 continue;
2791
2792 SCH_SHEET_PATH pin_path = path;
2793 pin_path.push_back( pin->GetParent() );
2794
2795 if( pin_path != aParent->m_sheet )
2796 continue;
2797
2798 if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
2799 {
2800 wxLogTrace( ConnTrace, wxS( "%lu: found additional parent %lu (%s)" ),
2801 aParent->m_code, candidate->m_code, candidate->m_driver_connection->Name() );
2802
2803 aParent->m_hier_children.insert( candidate );
2804 search_list.push_back( candidate );
2805 break;
2806 }
2807 }
2808 }
2809 }
2810 };
2811
2812 auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph )
2813 {
2814 // Sort bus neighbors by name to ensure deterministic processing order.
2815 // When multiple bus members (e.g., A0, A1, A2, A3) all connect to the same
2816 // shorted net in a child sheet, the first one processed "wins" and sets
2817 // the net name. Sorting ensures the alphabetically-first name is chosen.
2818 std::vector<std::shared_ptr<SCH_CONNECTION>> sortedMembers;
2819
2820 for( const auto& kv : aParentGraph->m_bus_neighbors )
2821 sortedMembers.push_back( kv.first );
2822
2823 std::sort( sortedMembers.begin(), sortedMembers.end(),
2824 []( const std::shared_ptr<SCH_CONNECTION>& a,
2825 const std::shared_ptr<SCH_CONNECTION>& b )
2826 {
2827 return a->Name() < b->Name();
2828 } );
2829
2830 for( const std::shared_ptr<SCH_CONNECTION>& member_conn : sortedMembers )
2831 {
2832 const auto& kv_it = aParentGraph->m_bus_neighbors.find( member_conn );
2833
2834 if( kv_it == aParentGraph->m_bus_neighbors.end() )
2835 continue;
2836
2837 for( CONNECTION_SUBGRAPH* neighbor : kv_it->second )
2838 {
2839 // May have been absorbed but won't have been deleted
2840 while( neighbor->m_absorbed )
2841 neighbor = neighbor->m_absorbed_by;
2842
2843 SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
2844
2845 // Now member may be out of date, since we just cloned the
2846 // connection from higher up in the hierarchy. We need to
2847 // figure out what the actual new connection is.
2848 SCH_CONNECTION* member = matchBusMember( parent, member_conn.get() );
2849
2850 if( !member )
2851 {
2852 // Try harder: we might match on a secondary driver
2853 for( CONNECTION_SUBGRAPH* sg : kv_it->second )
2854 {
2855 if( sg->m_multiple_drivers )
2856 {
2857 SCH_SHEET_PATH sheet = sg->m_sheet;
2858
2859 for( SCH_ITEM* driver : sg->m_drivers )
2860 {
2861 auto c = getDefaultConnection( driver, sg );
2862 member = matchBusMember( parent, c.get() );
2863
2864 if( member )
2865 break;
2866 }
2867 }
2868
2869 if( member )
2870 break;
2871 }
2872 }
2873
2874 // This is bad, probably an ERC error
2875 if( !member )
2876 {
2877 wxLogTrace( ConnTrace, wxS( "Could not match bus member %s in %s" ),
2878 member_conn->Name(), parent->Name() );
2879 continue;
2880 }
2881
2882 SCH_CONNECTION* neighbor_conn = neighbor->m_driver_connection;
2883
2884 wxCHECK2( neighbor_conn, continue );
2885
2886 wxString neighbor_name = neighbor_conn->Name();
2887
2888 // Matching name: no update needed
2889 if( neighbor_name == member->Name() )
2890 continue;
2891
2892 // Was this neighbor already updated from a different sheet? Don't rename it again,
2893 // unless this same parent bus updated it and the bus member name has since changed
2894 // (which can happen when a bus member is renamed via stale member update, issue #18299).
2895 if( neighbor_conn->Sheet() != neighbor->m_sheet )
2896 {
2897 // If the neighbor's connection sheet doesn't match this parent bus's sheet,
2898 // it was updated by a different bus entirely. Don't override.
2899 if( neighbor_conn->Sheet() != parent->Sheet() )
2900 continue;
2901
2902 // If the neighbor's connection sheet matches this parent bus's sheet but
2903 // the names differ, check if the neighbor's current name still matches
2904 // a member of this bus. If it does, the neighbor was updated by a different
2905 // member of this same bus and we should preserve that (determinism).
2906 // If it doesn't match any member, the bus member was renamed and we should update.
2907 SCH_CONNECTION temp( nullptr, neighbor->m_sheet );
2908 temp.ConfigureFromLabel( neighbor_name );
2909
2910 if( matchBusMember( parent, &temp ) )
2911 continue;
2912 }
2913
2914 // Safety check against infinite recursion
2915 wxCHECK2_MSG( neighbor_conn->IsNet(), continue,
2916 wxS( "\"" ) + neighbor_name + wxS( "\" is not a net." ) );
2917
2918 wxLogTrace( ConnTrace, wxS( "%lu (%s) connected to bus member %s (local %s)" ),
2919 neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
2920
2921 // Take whichever name is higher priority
2924 {
2925 member->Clone( *neighbor_conn );
2926 stale_bus_members.insert( member );
2927 }
2928 else
2929 {
2930 neighbor_conn->Clone( *member );
2931
2932 recacheSubgraphName( neighbor, neighbor_name );
2933
2934 // Recurse onto this neighbor in case it needs to re-propagate
2935 neighbor->m_dirty = true;
2936 propagateToNeighbors( neighbor, aForce );
2937
2938 // After hierarchy propagation, the neighbor's connection may have been
2939 // updated to a higher-priority driver (e.g., a power symbol discovered
2940 // through hierarchical sheet pins). If so, update the bus member to match.
2941 // This ensures that net names propagate correctly through bus connections
2942 // that span hierarchical boundaries (issue #18119).
2943 if( neighbor_conn->Name() != member->Name() )
2944 {
2945 member->Clone( *neighbor_conn );
2946 stale_bus_members.insert( member );
2947 }
2948 }
2949 }
2950 }
2951 };
2952
2953 // If we are a bus, we must propagate to local neighbors and then the hierarchy
2954 if( conn->IsBus() )
2955 propagate_bus_neighbors( aSubgraph );
2956
2957 // If we have both ports and pins, skip processing as we'll be visited by a parent or child.
2958 // If we only have one or the other, process (we can either go bottom-up or top-down depending
2959 // on which subgraph comes up first)
2960 if( !aForce && !aSubgraph->m_hier_ports.empty() && !aSubgraph->m_hier_pins.empty() )
2961 {
2962 wxLogTrace( ConnTrace, wxS( "%lu (%s) has both hier ports and pins; deferring processing" ),
2963 aSubgraph->m_code, conn->Name() );
2964 return;
2965 }
2966 else if( aSubgraph->m_hier_ports.empty() && aSubgraph->m_hier_pins.empty() )
2967 {
2968 wxLogTrace( ConnTrace, wxS( "%lu (%s) has no hier pins or ports on sheet %s; marking clean" ),
2969 aSubgraph->m_code, conn->Name(), aSubgraph->m_sheet.PathHumanReadable() );
2970 aSubgraph->m_dirty = false;
2971 return;
2972 }
2973
2974 visited.insert( aSubgraph );
2975
2976 wxLogTrace( ConnTrace, wxS( "Propagating %lu (%s) to subsheets" ),
2977 aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
2978
2979 visit( aSubgraph );
2980
2981 for( unsigned i = 0; i < search_list.size(); i++ )
2982 {
2983 auto child = search_list[i];
2984
2985 if( visited.insert( child ).second )
2986 visit( child );
2987
2988 child->m_dirty = false;
2989 }
2990
2991 // Now, find the best driver for this chain of subgraphs
2992 CONNECTION_SUBGRAPH* bestDriver = aSubgraph;
2994 bool bestIsStrong = ( highest >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
2995 wxString bestName = aSubgraph->m_driver_connection->Name();
2996
2997 // Check if a subsheet has a higher-priority connection to the same net
2999 {
3000 for( CONNECTION_SUBGRAPH* subgraph : visited )
3001 {
3002 if( subgraph == aSubgraph )
3003 continue;
3004
3006 CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
3007
3008 bool candidateStrong = ( priority >= CONNECTION_SUBGRAPH::PRIORITY::HIER_LABEL );
3009 wxString candidateName = subgraph->m_driver_connection->Name();
3010 bool shorterPath = subgraph->m_sheet.size() < bestDriver->m_sheet.size();
3011 bool asGoodPath = subgraph->m_sheet.size() <= bestDriver->m_sheet.size();
3012
3013 // Pick a better driving subgraph if it:
3014 // a) has a power pin or global driver
3015 // b) is a strong driver and we're a weak driver
3016 // c) is a higher priority strong driver
3017 // d) matches our priority, is a strong driver, and has a shorter path
3018 // e) matches our strength and is at least as short, and is alphabetically lower
3019
3021 ( !bestIsStrong && candidateStrong ) ||
3022 ( priority > highest && candidateStrong ) ||
3023 ( priority == highest && candidateStrong && shorterPath ) ||
3024 ( ( bestIsStrong == candidateStrong ) && asGoodPath && ( priority == highest ) &&
3025 ( candidateName < bestName ) ) )
3026 {
3027 bestDriver = subgraph;
3028 highest = priority;
3029 bestIsStrong = candidateStrong;
3030 bestName = candidateName;
3031 }
3032 }
3033 }
3034
3035 if( bestDriver != aSubgraph )
3036 {
3037 wxLogTrace( ConnTrace, wxS( "%lu (%s) overridden by new driver %lu (%s)" ),
3038 aSubgraph->m_code, aSubgraph->m_driver_connection->Name(), bestDriver->m_code,
3039 bestDriver->m_driver_connection->Name() );
3040 }
3041
3042 conn = bestDriver->m_driver_connection;
3043
3044 for( CONNECTION_SUBGRAPH* subgraph : visited )
3045 {
3046 wxString old_name = subgraph->m_driver_connection->Name();
3047
3048 subgraph->m_driver_connection->Clone( *conn );
3049
3050 if( old_name != conn->Name() )
3051 recacheSubgraphName( subgraph, old_name );
3052
3053 if( conn->IsBus() )
3054 propagate_bus_neighbors( subgraph );
3055 }
3056
3057 // Somewhere along the way, a bus member may have been upgraded to a global or power label.
3058 // Because this can happen anywhere, we need a second pass to update all instances of that bus
3059 // member to have the correct connection info
3060 if( conn->IsBus() && !stale_bus_members.empty() )
3061 {
3062 std::unordered_set<SCH_CONNECTION*> cached_members = stale_bus_members;
3063
3064 for( SCH_CONNECTION* stale_member : cached_members )
3065 {
3066 for( CONNECTION_SUBGRAPH* subgraph : visited )
3067 {
3068 SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection, stale_member );
3069
3070 if( !member )
3071 {
3072 wxLogTrace( ConnTrace, wxS( "WARNING: failed to match stale member %s in %s." ),
3073 stale_member->Name(), subgraph->m_driver_connection->Name() );
3074 continue;
3075 }
3076
3077 wxLogTrace( ConnTrace, wxS( "Updating %lu (%s) member %s to %s" ), subgraph->m_code,
3078 subgraph->m_driver_connection->Name(), member->LocalName(), stale_member->Name() );
3079
3080 member->Clone( *stale_member );
3081
3082 propagate_bus_neighbors( subgraph );
3083 }
3084 }
3085 }
3086
3087 aSubgraph->m_dirty = false;
3088}
3089
3090
3091std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
3092 CONNECTION_SUBGRAPH* aSubgraph )
3093{
3094 std::shared_ptr<SCH_CONNECTION> c = std::shared_ptr<SCH_CONNECTION>( nullptr );
3095
3096 switch( aItem->Type() )
3097 {
3098 case SCH_PIN_T:
3099 if( static_cast<SCH_PIN*>( aItem )->IsPower() )
3100 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
3101
3102 break;
3103
3104 case SCH_GLOBAL_LABEL_T:
3105 case SCH_HIER_LABEL_T:
3106 case SCH_LABEL_T:
3107 c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
3108 break;
3109
3110 default:
3111 break;
3112 }
3113
3114 if( c )
3115 {
3116 c->SetGraph( this );
3117 c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
3118 }
3119
3120 return c;
3121}
3122
3123
3125 SCH_CONNECTION* aSearch )
3126{
3127 // Should we return a null pointer if the connection is not a bus connection?
3128 wxASSERT( aBusConnection->IsBus() );
3129
3130 SCH_CONNECTION* match = nullptr;
3131
3132 if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
3133 {
3134 // Vector bus: compare against index, because we allow the name
3135 // to be different
3136
3137 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : aBusConnection->Members() )
3138 {
3139 if( bus_member->VectorIndex() == aSearch->VectorIndex() )
3140 {
3141 match = bus_member.get();
3142 break;
3143 }
3144 }
3145 }
3146 else
3147 {
3148 // Group bus
3149 for( const std::shared_ptr<SCH_CONNECTION>& c : aBusConnection->Members() )
3150 {
3151 // Vector inside group: compare names, because for bus groups
3152 // we expect the naming to be consistent across all usages
3153 // TODO(JE) explain this in the docs
3154 if( c->Type() == CONNECTION_TYPE::BUS )
3155 {
3156 for( const std::shared_ptr<SCH_CONNECTION>& bus_member : c->Members() )
3157 {
3158 if( bus_member->LocalName() == aSearch->LocalName() )
3159 {
3160 match = bus_member.get();
3161 break;
3162 }
3163 }
3164 }
3165 else if( c->LocalName() == aSearch->LocalName() )
3166 {
3167 match = c.get();
3168 break;
3169 }
3170 }
3171 }
3172
3173 return match;
3174}
3175
3176
3177void CONNECTION_GRAPH::recacheSubgraphName( CONNECTION_SUBGRAPH* aSubgraph, const wxString& aOldName )
3178{
3179 auto it = m_net_name_to_subgraphs_map.find( aOldName );
3180
3181 if( it != m_net_name_to_subgraphs_map.end() )
3182 {
3183 std::vector<CONNECTION_SUBGRAPH*>& vec = it->second;
3184 std::erase( vec, aSubgraph );
3185 }
3186
3187 wxLogTrace( ConnTrace, wxS( "recacheSubgraphName: %s => %s" ), aOldName,
3188 aSubgraph->m_driver_connection->Name() );
3189
3190 m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
3191}
3192
3193
3194std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
3195{
3196 auto it = m_bus_alias_cache.find( aName );
3197
3198 return it != m_bus_alias_cache.end() ? it->second : nullptr;
3199}
3200
3201
3202std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
3203{
3204 std::vector<const CONNECTION_SUBGRAPH*> ret;
3205
3206 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3207 {
3208 // Graph is supposed to be up-to-date before calling this
3209 // Should we continue if the subgraph is not up to date?
3210 wxASSERT( !subgraph->m_dirty );
3211
3212 if( !subgraph->m_driver )
3213 continue;
3214
3215 SCH_SHEET_PATH* sheet = &subgraph->m_sheet;
3216 SCH_CONNECTION* connection = subgraph->m_driver->Connection( sheet );
3217
3218 if( !connection->IsBus() )
3219 continue;
3220
3221 auto labels = subgraph->GetVectorBusLabels();
3222
3223 if( labels.size() > 1 )
3224 {
3225 bool different = false;
3226 wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText( sheet, false );
3227
3228 for( unsigned i = 1; i < labels.size(); ++i )
3229 {
3230 if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText( sheet, false ) != first )
3231 {
3232 different = true;
3233 break;
3234 }
3235 }
3236
3237 if( !different )
3238 continue;
3239
3240 wxLogTrace( ConnTrace, wxS( "SG %ld (%s) has multiple bus labels" ), subgraph->m_code,
3241 connection->Name() );
3242
3243 ret.push_back( subgraph );
3244 }
3245 }
3246
3247 return ret;
3248}
3249
3250
3252{
3253 wxString retval = aSubGraph->GetNetName();
3254 bool found = false;
3255
3256 // This is a hacky way to find the true subgraph net name (why do we not store it?)
3257 // TODO: Remove once the actual netname of the subgraph is stored with the subgraph
3258
3259 for( auto it = m_net_name_to_subgraphs_map.begin();
3260 it != m_net_name_to_subgraphs_map.end() && !found; ++it )
3261 {
3262 for( CONNECTION_SUBGRAPH* graph : it->second )
3263 {
3264 if( graph == aSubGraph )
3265 {
3266 retval = it->first;
3267 found = true;
3268 break;
3269 }
3270 }
3271 }
3272
3273 return retval;
3274}
3275
3276
3278 const SCH_SHEET_PATH& aPath )
3279{
3280 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3281
3282 if( it == m_net_name_to_subgraphs_map.end() )
3283 return nullptr;
3284
3285 for( CONNECTION_SUBGRAPH* sg : it->second )
3286 {
3287 // Cache is supposed to be valid by now
3288 // Should we continue if the cache is not valid?
3289 wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
3290
3291 if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
3292 return sg;
3293 }
3294
3295 return nullptr;
3296}
3297
3298
3300{
3301 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3302
3303 if( it == m_net_name_to_subgraphs_map.end() )
3304 return nullptr;
3305
3306 // Should this return a nullptr if the map entry is empty?
3307 wxASSERT( !it->second.empty() );
3308
3309 return it->second[0];
3310}
3311
3312
3314{
3315 auto it = m_item_to_subgraph_map.find( aItem );
3316 CONNECTION_SUBGRAPH* ret = it != m_item_to_subgraph_map.end() ? it->second : nullptr;
3317
3318 while( ret && ret->m_absorbed )
3319 ret = ret->m_absorbed_by;
3320
3321 return ret;
3322}
3323
3324
3325const std::vector<CONNECTION_SUBGRAPH*>&
3326CONNECTION_GRAPH::GetAllSubgraphs( const wxString& aNetName ) const
3327{
3328 static const std::vector<CONNECTION_SUBGRAPH*> subgraphs;
3329
3330 auto it = m_net_name_to_subgraphs_map.find( aNetName );
3331
3332 if( it == m_net_name_to_subgraphs_map.end() )
3333 return subgraphs;
3334
3335 return it->second;
3336}
3337
3338
3340{
3341 int error_count = 0;
3342
3343 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::RunERC" ) );
3344
3345 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3346
3347 // We don't want to run many ERC checks more than once on a given screen even though it may
3348 // represent multiple sheets with multiple subgraphs. We can tell these apart by drivers.
3349 std::set<SCH_ITEM*> seenDriverInstances;
3350
3351 for( CONNECTION_SUBGRAPH* subgraph : m_subgraphs )
3352 {
3353 // There shouldn't be any null sub-graph pointers.
3354 wxCHECK2( subgraph, continue );
3355
3356 // Graph is supposed to be up-to-date before calling RunERC()
3357 // Should we continue if the subgraph is not up to date?
3358 wxASSERT( !subgraph->m_dirty );
3359
3360 if( subgraph->m_absorbed )
3361 continue;
3362
3363 if( seenDriverInstances.count( subgraph->m_driver ) )
3364 continue;
3365
3366 if( subgraph->m_driver )
3367 seenDriverInstances.insert( subgraph->m_driver );
3368
3379 if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) )
3380 {
3381 if( !ercCheckMultipleDrivers( subgraph ) )
3382 error_count++;
3383 }
3384
3385 subgraph->ResolveDrivers( false );
3386
3387 if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT ) )
3388 {
3389 if( !ercCheckBusToNetConflicts( subgraph ) )
3390 error_count++;
3391 }
3392
3393 if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT ) )
3394 {
3395 if( !ercCheckBusToBusEntryConflicts( subgraph ) )
3396 error_count++;
3397 }
3398
3399 if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT ) )
3400 {
3401 if( !ercCheckBusToBusConflicts( subgraph ) )
3402 error_count++;
3403 }
3404
3405 if( settings.IsTestEnabled( ERCE_WIRE_DANGLING ) )
3406 {
3407 if( !ercCheckFloatingWires( subgraph ) )
3408 error_count++;
3409 }
3410
3412 {
3413 if( !ercCheckDanglingWireEndpoints( subgraph ) )
3414 error_count++;
3415 }
3416
3419 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3420 {
3421 if( !ercCheckNoConnects( subgraph ) )
3422 error_count++;
3423 }
3424
3426 || settings.IsTestEnabled( ERCE_LABEL_SINGLE_PIN ) )
3427 {
3428 if( !ercCheckLabels( subgraph ) )
3429 error_count++;
3430 }
3431 }
3432
3433 if( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED ) )
3434 {
3435 error_count += ercCheckDirectiveLabels();
3436 }
3437
3438 // Hierarchical sheet checking is done at the schematic level
3440 || settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3441 {
3442 error_count += ercCheckHierSheets();
3443 }
3444
3445 if( settings.IsTestEnabled( ERCE_SINGLE_GLOBAL_LABEL ) )
3446 {
3447 error_count += ercCheckSingleGlobalLabel();
3448 }
3449
3450 return error_count;
3451}
3452
3453
3455{
3456 wxCHECK( aSubgraph, false );
3457
3458 if( aSubgraph->m_multiple_drivers )
3459 {
3460 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3461 {
3462 if( driver == aSubgraph->m_driver )
3463 continue;
3464
3465 if( driver->Type() == SCH_GLOBAL_LABEL_T
3466 || driver->Type() == SCH_HIER_LABEL_T
3467 || driver->Type() == SCH_LABEL_T
3468 || ( driver->Type() == SCH_PIN_T && static_cast<SCH_PIN*>( driver )->IsPower() ) )
3469 {
3470 const wxString& primaryName = aSubgraph->GetNameForDriver( aSubgraph->m_driver );
3471 const wxString& secondaryName = aSubgraph->GetNameForDriver( driver );
3472
3473 if( primaryName == secondaryName )
3474 continue;
3475
3476 wxString msg = wxString::Format( _( "Both %s and %s are attached to the same "
3477 "items; %s will be used in the netlist" ),
3478 primaryName, secondaryName, primaryName );
3479
3480 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
3481 ercItem->SetItems( aSubgraph->m_driver, driver );
3482 ercItem->SetSheetSpecificPath( aSubgraph->GetSheet() );
3483 ercItem->SetItemsSheetPaths( aSubgraph->GetSheet(), aSubgraph->m_sheet );
3484 ercItem->SetErrorMessage( msg );
3485
3486 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), driver->GetPosition() );
3487 aSubgraph->m_sheet.LastScreen()->Append( marker );
3488
3489 return false;
3490 }
3491 }
3492 }
3493
3494 return true;
3495}
3496
3497
3499{
3500 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3501 SCH_SCREEN* screen = sheet.LastScreen();
3502
3503 SCH_ITEM* net_item = nullptr;
3504 SCH_ITEM* bus_item = nullptr;
3505 SCH_CONNECTION conn( this );
3506
3507 for( SCH_ITEM* item : aSubgraph->m_items )
3508 {
3509 switch( item->Type() )
3510 {
3511 case SCH_LINE_T:
3512 {
3513 if( item->GetLayer() == LAYER_BUS )
3514 bus_item = ( !bus_item ) ? item : bus_item;
3515 else
3516 net_item = ( !net_item ) ? item : net_item;
3517
3518 break;
3519 }
3520
3521 case SCH_LABEL_T:
3522 case SCH_GLOBAL_LABEL_T:
3523 case SCH_SHEET_PIN_T:
3524 case SCH_HIER_LABEL_T:
3525 {
3526 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
3527 conn.ConfigureFromLabel( EscapeString( text->GetShownText( &sheet, false ), CTX_NETNAME ) );
3528
3529 if( conn.IsBus() )
3530 bus_item = ( !bus_item ) ? item : bus_item;
3531 else
3532 net_item = ( !net_item ) ? item : net_item;
3533
3534 break;
3535 }
3536
3537 default:
3538 break;
3539 }
3540 }
3541
3542 if( net_item && bus_item )
3543 {
3544 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
3545 ercItem->SetSheetSpecificPath( sheet );
3546 ercItem->SetItems( net_item, bus_item );
3547
3548 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), net_item->GetPosition() );
3549 screen->Append( marker );
3550
3551 return false;
3552 }
3553
3554 return true;
3555}
3556
3557
3559{
3560 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3561 SCH_SCREEN* screen = sheet.LastScreen();
3562
3563 SCH_ITEM* label = nullptr;
3564 SCH_ITEM* port = nullptr;
3565
3566 for( SCH_ITEM* item : aSubgraph->m_items )
3567 {
3568 switch( item->Type() )
3569 {
3570 case SCH_TEXT_T:
3571 case SCH_GLOBAL_LABEL_T:
3572 if( !label && item->Connection( &sheet )->IsBus() )
3573 label = item;
3574 break;
3575
3576 case SCH_SHEET_PIN_T:
3577 case SCH_HIER_LABEL_T:
3578 if( !port && item->Connection( &sheet )->IsBus() )
3579 port = item;
3580 break;
3581
3582 default:
3583 break;
3584 }
3585 }
3586
3587 if( label && port )
3588 {
3589 bool match = false;
3590
3591 for( const auto& member : label->Connection( &sheet )->Members() )
3592 {
3593 for( const auto& test : port->Connection( &sheet )->Members() )
3594 {
3595 if( test != member && member->Name() == test->Name() )
3596 {
3597 match = true;
3598 break;
3599 }
3600 }
3601
3602 if( match )
3603 break;
3604 }
3605
3606 if( !match )
3607 {
3608 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
3609 ercItem->SetSheetSpecificPath( sheet );
3610 ercItem->SetItems( label, port );
3611
3612 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), label->GetPosition() );
3613 screen->Append( marker );
3614
3615 return false;
3616 }
3617 }
3618
3619 return true;
3620}
3621
3622
3624{
3625 bool conflict = false;
3626 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3627 SCH_SCREEN* screen = sheet.LastScreen();
3628
3629 SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
3630 SCH_ITEM* bus_wire = nullptr;
3631 wxString bus_name;
3632
3633 if( !aSubgraph->m_driver_connection )
3634 {
3635 // Incomplete bus entry. Let the unconnected tests handle it.
3636 return true;
3637 }
3638
3639 for( SCH_ITEM* item : aSubgraph->m_items )
3640 {
3641 switch( item->Type() )
3642 {
3644 if( !bus_entry )
3645 bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
3646
3647 break;
3648
3649 default:
3650 break;
3651 }
3652 }
3653
3654 if( bus_entry && bus_entry->m_connected_bus_item )
3655 {
3656 bus_wire = bus_entry->m_connected_bus_item;
3657
3658 // Should we continue if the type is not a line?
3659 wxASSERT( bus_wire->Type() == SCH_LINE_T );
3660
3661 // In some cases, the connection list (SCH_CONNECTION*) can be null.
3662 // Skip null connections.
3663 if( bus_entry->Connection( &sheet )
3664 && bus_wire->Type() == SCH_LINE_T
3665 && bus_wire->Connection( &sheet ) )
3666 {
3667 conflict = true; // Assume a conflict; we'll reset if we find it's OK
3668
3669 bus_name = bus_wire->Connection( &sheet )->Name();
3670
3671 std::set<wxString> test_names;
3672 test_names.insert( bus_entry->Connection( &sheet )->FullLocalName() );
3673
3674 wxString baseName = sheet.PathHumanReadable();
3675
3676 for( SCH_ITEM* driver : aSubgraph->m_drivers )
3677 test_names.insert( baseName + aSubgraph->GetNameForDriver( driver ) );
3678
3679 for( const auto& member : bus_wire->Connection( &sheet )->Members() )
3680 {
3681 if( member->Type() == CONNECTION_TYPE::BUS )
3682 {
3683 for( const auto& sub_member : member->Members() )
3684 {
3685 if( test_names.count( sub_member->FullLocalName() ) )
3686 conflict = false;
3687 }
3688 }
3689 else if( test_names.count( member->FullLocalName() ) )
3690 {
3691 conflict = false;
3692 }
3693 }
3694 }
3695 }
3696
3697 // Don't report warnings if this bus member has been overridden by a higher priority power pin
3698 // or global label
3699 if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
3701 {
3702 conflict = false;
3703 }
3704
3705 if( conflict )
3706 {
3707 wxString netName = aSubgraph->m_driver_connection->Name();
3708 wxString msg = wxString::Format( _( "Net %s is graphically connected to bus %s but is not a"
3709 " member of that bus" ),
3710 UnescapeString( netName ),
3711 UnescapeString( bus_name ) );
3712 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
3713 ercItem->SetSheetSpecificPath( sheet );
3714 ercItem->SetItems( bus_entry, bus_wire );
3715 ercItem->SetErrorMessage( msg );
3716
3717 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), bus_entry->GetPosition() );
3718 screen->Append( marker );
3719
3720 return false;
3721 }
3722
3723 return true;
3724}
3725
3726
3728{
3729 ERC_SETTINGS& settings = m_schematic->ErcSettings();
3730 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3731 SCH_SCREEN* screen = sheet.LastScreen();
3732 bool ok = true;
3733 SCH_PIN* pin = nullptr;
3734
3735 std::set<SCH_PIN*> unique_pins;
3736 std::set<SCH_LABEL_BASE*> unique_labels;
3737
3738 wxString netName = GetResolvedSubgraphName( aSubgraph );
3739
3740 auto process_subgraph = [&]( const CONNECTION_SUBGRAPH* aProcessGraph )
3741 {
3742 // Any subgraph that contains a no-connect should not
3743 // more than one pin (which would indicate it is connected
3744 for( SCH_ITEM* item : aProcessGraph->m_items )
3745 {
3746 switch( item->Type() )
3747 {
3748 case SCH_PIN_T:
3749 {
3750 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3751
3752 // Only link NC to pin on the current subgraph being checked
3753 if( aProcessGraph == aSubgraph )
3754 pin = test_pin;
3755
3756 if( std::none_of( unique_pins.begin(), unique_pins.end(),
3757 [test_pin]( SCH_PIN* aPin )
3758 {
3759 return test_pin->IsStacked( aPin );
3760 }
3761 ))
3762 {
3763 unique_pins.insert( test_pin );
3764 }
3765
3766 break;
3767 }
3768
3769 case SCH_LABEL_T:
3770 case SCH_GLOBAL_LABEL_T:
3771 case SCH_HIER_LABEL_T:
3772 unique_labels.insert( static_cast<SCH_LABEL_BASE*>( item ) );
3774 default:
3775 break;
3776 }
3777 }
3778 };
3779
3780 auto it = m_net_name_to_subgraphs_map.find( netName );
3781
3782 if( it != m_net_name_to_subgraphs_map.end() )
3783 {
3784 for( const CONNECTION_SUBGRAPH* subgraph : it->second )
3785 {
3786 process_subgraph( subgraph );
3787 }
3788 }
3789 else
3790 {
3791 process_subgraph( aSubgraph );
3792 }
3793
3794 if( aSubgraph->m_no_connect != nullptr )
3795 {
3796 // Special case: If the subgraph being checked consists of only a hier port/pin and
3797 // a no-connect, we don't issue a "no-connect connected" warning just because
3798 // connections exist on the sheet on the other side of the link.
3799 VECTOR2I noConnectPos = aSubgraph->m_no_connect->GetPosition();
3800
3801 for( SCH_SHEET_PIN* hierPin : aSubgraph->m_hier_pins )
3802 {
3803 if( hierPin->GetPosition() == noConnectPos )
3804 return true;
3805 }
3806
3807 for( SCH_HIERLABEL* hierLabel : aSubgraph->m_hier_ports )
3808 {
3809 if( hierLabel->GetPosition() == noConnectPos )
3810 return true;
3811 }
3812
3813 for( SCH_ITEM* item : screen->Items().Overlapping( SCH_SYMBOL_T, noConnectPos ) )
3814 {
3815 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
3816
3817 const SCH_PIN* test_pin = symbol->GetPin( noConnectPos );
3818
3819 if( test_pin && test_pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
3820 return true;
3821 }
3822
3823 if( unique_pins.size() > 1 && settings.IsTestEnabled( ERCE_NOCONNECT_CONNECTED ) )
3824 {
3825 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
3826 ercItem->SetSheetSpecificPath( sheet );
3827 ercItem->SetItemsSheetPaths( sheet );
3828
3829 VECTOR2I pos;
3830
3831 if( pin )
3832 {
3833 ercItem->SetItems( pin, aSubgraph->m_no_connect );
3834 pos = pin->GetPosition();
3835 }
3836 else
3837 {
3838 ercItem->SetItems( aSubgraph->m_no_connect );
3839 pos = aSubgraph->m_no_connect->GetPosition();
3840 }
3841
3842 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pos );
3843 screen->Append( marker );
3844
3845 ok = false;
3846 }
3847
3848 if( unique_pins.empty() && unique_labels.empty() &&
3850 {
3851 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
3852 ercItem->SetItems( aSubgraph->m_no_connect );
3853 ercItem->SetSheetSpecificPath( sheet );
3854 ercItem->SetItemsSheetPaths( sheet );
3855
3856 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), aSubgraph->m_no_connect->GetPosition() );
3857 screen->Append( marker );
3858
3859 ok = false;
3860 }
3861 }
3862 else
3863 {
3864 bool has_other_connections = false;
3865 std::vector<SCH_PIN*> pins;
3866
3867 // Any subgraph that lacks a no-connect and contains a pin should also
3868 // contain at least one other potential driver
3869
3870 for( SCH_ITEM* item : aSubgraph->m_items )
3871 {
3872 switch( item->Type() )
3873 {
3874 case SCH_PIN_T:
3875 {
3876 SCH_PIN* test_pin = static_cast<SCH_PIN*>( item );
3877
3878 // Stacked pins do not count as other connections but non-stacked pins do
3879 if( !has_other_connections && !pins.empty()
3880 && !test_pin->GetParentSymbol()->IsPower() )
3881 {
3882 for( SCH_PIN* other_pin : pins )
3883 {
3884 if( !test_pin->IsStacked( other_pin ) )
3885 {
3886 has_other_connections = true;
3887 break;
3888 }
3889 }
3890 }
3891
3892 pins.emplace_back( static_cast<SCH_PIN*>( item ) );
3893
3894 break;
3895 }
3896
3897 default:
3898 if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
3899 has_other_connections = true;
3900
3901 break;
3902 }
3903 }
3904
3905 // For many checks, we can just use the first pin
3906 pin = pins.empty() ? nullptr : pins[0];
3907
3908 // But if there is a power pin, it might be connected elsewhere
3909 for( SCH_PIN* test_pin : pins )
3910 {
3911 // Prefer the pin is part of a real component rather than some stray power symbol
3912 // Or else we may fail walking connected components to a power symbol pin since we
3913 // reject starting at a power symbol
3914 if( test_pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN && !test_pin->IsPower() )
3915 {
3916 pin = test_pin;
3917 break;
3918 }
3919 }
3920
3921 // Check if power input pins connect to anything else via net name,
3922 // but not for power symbols (with visible or legacy invisible pins).
3923 // We want to throw unconnected errors for power symbols even if they are connected to other
3924 // net items by name, because usually failing to connect them graphically is a mistake
3925 SYMBOL* pinLibParent = ( pin && pin->GetLibPin() )
3926 ? pin->GetLibPin()->GetParentSymbol() : nullptr;
3927
3928 if( pin && !has_other_connections
3929 && !pin->IsPower()
3930 && ( !pinLibParent || !pinLibParent->IsPower() ) )
3931 {
3932 wxString name = pin->Connection( &sheet )->Name();
3933 wxString local_name = pin->Connection( &sheet )->Name( true );
3934
3935 if( m_global_label_cache.count( name )
3936 || m_local_label_cache.count( std::make_pair( sheet, local_name ) ) )
3937 {
3938 has_other_connections = true;
3939 }
3940 }
3941
3942 // Only one pin, and it's not a no-connect pin
3943 if( pin && !has_other_connections
3944 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC
3945 && pin->GetType() != ELECTRICAL_PINTYPE::PT_NIC
3946 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3947 {
3948 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3949 ercItem->SetSheetSpecificPath( sheet );
3950 ercItem->SetItemsSheetPaths( sheet );
3951 ercItem->SetItems( pin );
3952
3953 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pin->GetPosition() );
3954 screen->Append( marker );
3955
3956 ok = false;
3957 }
3958
3959 // If there are multiple pins in this SG, they might be indirectly connected (by netname)
3960 // rather than directly connected (by wires). We want to flag dangling pins even if they
3961 // join nets with another pin, as it's often a mistake
3962 if( pins.size() > 1 )
3963 {
3964 for( SCH_PIN* testPin : pins )
3965 {
3966 // We only apply this test to power symbols, because other symbols have
3967 // pins that are meant to be dangling, but the power symbols have pins
3968 // that are *not* meant to be dangling.
3969 SYMBOL* testLibParent = testPin->GetLibPin()
3970 ? testPin->GetLibPin()->GetParentSymbol()
3971 : nullptr;
3972
3973 if( testLibParent && testLibParent->IsPower()
3974 && testPin->ConnectedItems( sheet ).empty()
3975 && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
3976 {
3977 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
3978 ercItem->SetSheetSpecificPath( sheet );
3979 ercItem->SetItemsSheetPaths( sheet );
3980 ercItem->SetItems( testPin );
3981
3982 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), testPin->GetPosition() );
3983 screen->Append( marker );
3984
3985 ok = false;
3986 }
3987 }
3988 }
3989 }
3990
3991 return ok;
3992}
3993
3994
3996{
3997 int err_count = 0;
3998 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
3999
4000 for( SCH_ITEM* item : aSubgraph->m_items )
4001 {
4002 if( item->GetLayer() != LAYER_WIRE )
4003 continue;
4004
4005 if( item->Type() == SCH_LINE_T )
4006 {
4007 SCH_LINE* line = static_cast<SCH_LINE*>( item );
4008
4009 if( line->IsGraphicLine() )
4010 continue;
4011
4012 auto report_error = [&]( VECTOR2I& location )
4013 {
4014 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_UNCONNECTED_WIRE_ENDPOINT );
4015
4016 ercItem->SetItems( line );
4017 ercItem->SetSheetSpecificPath( sheet );
4018 ercItem->SetErrorMessage( _( "Unconnected wire endpoint" ) );
4019
4020 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), location );
4021 sheet.LastScreen()->Append( marker );
4022
4023 err_count++;
4024 };
4025
4026 if( line->IsStartDangling() )
4027 report_error( line->GetConnectionPoints()[0] );
4028
4029 if( line->IsEndDangling() )
4030 report_error( line->GetConnectionPoints()[1] );
4031 }
4032 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
4033 {
4034 SCH_BUS_WIRE_ENTRY* entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
4035
4036 auto report_error = [&]( VECTOR2I& location )
4037 {
4038 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_UNCONNECTED_WIRE_ENDPOINT );
4039
4040 ercItem->SetItems( entry );
4041 ercItem->SetSheetSpecificPath( sheet );
4042 ercItem->SetErrorMessage( _( "Unconnected wire to bus entry" ) );
4043
4044 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), location );
4045 sheet.LastScreen()->Append( marker );
4046
4047 err_count++;
4048 };
4049
4050 if( entry->IsStartDangling() )
4051 report_error( entry->GetConnectionPoints()[0] );
4052
4053 if( entry->IsEndDangling() )
4054 report_error( entry->GetConnectionPoints()[1] );
4055 }
4056
4057 }
4058
4059 return err_count > 0;
4060}
4061
4062
4064{
4065 if( aSubgraph->m_driver )
4066 return true;
4067
4068 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
4069 std::vector<SCH_ITEM*> wires;
4070
4071 // We've gotten this far, so we know we have no valid driver. All we need to do is check
4072 // for a wire that we can place the error on.
4073 for( SCH_ITEM* item : aSubgraph->m_items )
4074 {
4075 if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
4076 wires.emplace_back( item );
4077 else if( item->Type() == SCH_BUS_WIRE_ENTRY_T )
4078 wires.emplace_back( item );
4079 }
4080
4081 if( !wires.empty() )
4082 {
4083 SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
4084
4085 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
4086 ercItem->SetSheetSpecificPath( sheet );
4087 ercItem->SetItems( wires[0],
4088 wires.size() > 1 ? wires[1] : nullptr,
4089 wires.size() > 2 ? wires[2] : nullptr,
4090 wires.size() > 3 ? wires[3] : nullptr );
4091
4092 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), wires[0]->GetPosition() );
4093 screen->Append( marker );
4094
4095 return false;
4096 }
4097
4098 return true;
4099}
4100
4101
4103{
4104 // Label connection rules:
4105 // Any label without a no-connect needs to have at least 2 pins, otherwise it is invalid
4106 // Local labels are flagged if they don't connect to any pins and don't have a no-connect
4107 // Global labels are flagged if they appear only once, don't connect to any local labels,
4108 // and don't have a no-connect marker
4109
4110 if( !aSubgraph->m_driver_connection )
4111 return true;
4112
4113 // Buses are excluded from this test: many users create buses with only a single instance
4114 // and it's not really a problem as long as the nets in the bus pass ERC
4115 if( aSubgraph->m_driver_connection->IsBus() )
4116 return true;
4117
4118 const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
4119 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4120 bool ok = true;
4121 size_t pinCount = 0;
4122 bool has_nc = !!aSubgraph->m_no_connect;
4123
4124 std::map<KICAD_T, std::vector<SCH_TEXT*>> label_map;
4125
4126
4127 auto hasPins =
4128 []( const CONNECTION_SUBGRAPH* aLocSubgraph ) -> size_t
4129 {
4130 return std::count_if( aLocSubgraph->m_items.begin(), aLocSubgraph->m_items.end(),
4131 []( const SCH_ITEM* item )
4132 {
4133 return item->Type() == SCH_PIN_T;
4134 } );
4135 };
4136
4137 auto reportError =
4138 [&]( SCH_TEXT* aText, int errCode )
4139 {
4140 if( settings.IsTestEnabled( errCode ) )
4141 {
4142 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( errCode );
4143 ercItem->SetSheetSpecificPath( sheet );
4144 ercItem->SetItems( aText );
4145
4146 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), aText->GetPosition() );
4147 aSubgraph->m_sheet.LastScreen()->Append( marker );
4148 }
4149 };
4150
4151 pinCount = hasPins( aSubgraph );
4152
4153 for( SCH_ITEM* item : aSubgraph->m_items )
4154 {
4155 switch( item->Type() )
4156 {
4157 case SCH_LABEL_T:
4158 case SCH_GLOBAL_LABEL_T:
4159 case SCH_HIER_LABEL_T:
4160 {
4161 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
4162
4163 label_map[item->Type()].push_back( text );
4164
4165 // Below, we'll create an ERC if the whole subgraph is unconnected. But, additionally,
4166 // we want to error if an individual label in the subgraph is floating, even if it's
4167 // connected to other valid things by way of another label on the same sheet.
4168 if( text->IsDangling() )
4169 {
4170 reportError( text, ERCE_LABEL_NOT_CONNECTED );
4171 return false;
4172 }
4173
4174 break;
4175 }
4176
4177 default:
4178 break;
4179 }
4180 }
4181
4182 if( label_map.empty() )
4183 return true;
4184
4185 // No-connects on net neighbors will be noticed before, but to notice them on bus parents we
4186 // need to walk the graph
4187 for( auto& [ connection, subgraphs ] : aSubgraph->m_bus_parents )
4188 {
4189 for( CONNECTION_SUBGRAPH* busParent : subgraphs )
4190 {
4191 if( busParent->m_no_connect )
4192 {
4193 has_nc = true;
4194 break;
4195 }
4196
4197 CONNECTION_SUBGRAPH* hp = busParent->m_hier_parent;
4198
4199 while( hp )
4200 {
4201 if( hp->m_no_connect )
4202 {
4203 has_nc = true;
4204 break;
4205 }
4206
4207 hp = hp->m_hier_parent;
4208 }
4209 }
4210 }
4211
4212 wxString netName = GetResolvedSubgraphName( aSubgraph );
4213
4214 wxCHECK_MSG( m_schematic, true, wxS( "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" ) );
4215
4216 // Labels that have multiple pins connected are not dangling (may be used for naming segments)
4217 // so leave them without errors here
4218 if( pinCount > 1 )
4219 return true;
4220
4221 for( auto& [type, label_vec] : label_map )
4222 {
4223 for( SCH_TEXT* text : label_vec )
4224 {
4225 size_t allPins = pinCount;
4226 size_t localPins = pinCount;
4227
4228 // For local labels that are bus members, track local pins separately.
4229 // A local label connected to a bus that crosses hierarchy boundaries should
4230 // still have a local connection to component pins. Without this check, a label
4231 // that only connects to pins through the hierarchical bus would not be flagged.
4232 bool isBusMemberLabel = ( type == SCH_LABEL_T ) && !aSubgraph->m_bus_parents.empty();
4233
4234 auto it = m_net_name_to_subgraphs_map.find( netName );
4235
4236 if( it != m_net_name_to_subgraphs_map.end() )
4237 {
4238 for( const CONNECTION_SUBGRAPH* neighbor : it->second )
4239 {
4240 if( neighbor == aSubgraph )
4241 continue;
4242
4243 if( neighbor->m_no_connect )
4244 has_nc = true;
4245
4246 size_t neighborPins = hasPins( neighbor );
4247 allPins += neighborPins;
4248
4249 if( neighbor->m_sheet == sheet )
4250 localPins += neighborPins;
4251 }
4252 }
4253
4254 if( allPins == 1 && !has_nc )
4255 {
4256 reportError( text, ERCE_LABEL_SINGLE_PIN );
4257 ok = false;
4258 }
4259
4260 // For local bus member labels, check that there's at least one local pin connection.
4261 // Labels that only connect to pins through a hierarchical bus should be flagged.
4262 if( allPins == 0 || ( isBusMemberLabel && localPins == 0 && !has_nc ) )
4263 {
4264 reportError( text, ERCE_LABEL_NOT_CONNECTED );
4265 ok = false;
4266 }
4267 }
4268 }
4269
4270 return ok;
4271}
4272
4273
4275{
4276 int errors = 0;
4277
4278 std::map<wxString, std::tuple<int, const SCH_ITEM*, SCH_SHEET_PATH>> labelData;
4279
4280 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4281 {
4282 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GLOBAL_LABEL_T ) )
4283 {
4284 SCH_TEXT* labelText = static_cast<SCH_TEXT*>( item );
4285 wxString resolvedLabelText =
4286 EscapeString( labelText->GetShownText( &sheet, false ), CTX_NETNAME );
4287
4288 if( labelData.find( resolvedLabelText ) == labelData.end() )
4289 {
4290 labelData[resolvedLabelText] = { 1, item, sheet };
4291 }
4292 else
4293 {
4294 std::get<0>( labelData[resolvedLabelText] ) += 1;
4295 std::get<1>( labelData[resolvedLabelText] ) = nullptr;
4296 std::get<2>( labelData[resolvedLabelText] ) = sheet;
4297 }
4298 }
4299 }
4300
4301 for( const auto& label : labelData )
4302 {
4303 if( std::get<0>( label.second ) == 1 )
4304 {
4305 const SCH_SHEET_PATH& sheet = std::get<2>( label.second );
4306 const SCH_ITEM* item = std::get<1>( label.second );
4307
4308 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_SINGLE_GLOBAL_LABEL );
4309 ercItem->SetItems( std::get<1>( label.second ) );
4310 ercItem->SetSheetSpecificPath( sheet );
4311 ercItem->SetItemsSheetPaths( sheet );
4312
4313 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), item->GetPosition() );
4314 sheet.LastScreen()->Append( marker );
4315
4316 errors++;
4317 }
4318 }
4319
4320 return errors;
4321}
4322
4323
4325{
4326 int error_count = 0;
4327
4328 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4329 {
4330 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_DIRECTIVE_LABEL_T ) )
4331 {
4332 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
4333
4334 if( label->IsDangling() )
4335 {
4336 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_LABEL_NOT_CONNECTED );
4337 SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
4338 ercItem->SetSheetSpecificPath( sheet );
4339 ercItem->SetItems( text );
4340
4341 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), text->GetPosition() );
4342 sheet.LastScreen()->Append( marker );
4343 error_count++;
4344 }
4345 }
4346 }
4347
4348 return error_count;
4349}
4350
4351
4353{
4354 wxString msg;
4355 int errors = 0;
4356
4357 ERC_SETTINGS& settings = m_schematic->ErcSettings();
4358
4359 for( const SCH_SHEET_PATH& sheet : m_sheetList )
4360 {
4361 // Hierarchical labels in the top-level sheets cannot be connected to anything.
4362 if( sheet.Last()->IsTopLevelSheet() )
4363 {
4364 for( const SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
4365 {
4366 const SCH_HIERLABEL* label = static_cast<const SCH_HIERLABEL*>( item );
4367
4368 wxCHECK2( label, continue );
4369
4370 msg.Printf( _( "Hierarchical label '%s' in root sheet cannot be connected to non-existent "
4371 "parent sheet" ),
4372 label->GetShownText( &sheet, true ) );
4373 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4374 ercItem->SetItems( item );
4375 ercItem->SetErrorMessage( msg );
4376
4377 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), item->GetPosition() );
4378 sheet.LastScreen()->Append( marker );
4379
4380 errors++;
4381 }
4382 }
4383
4384 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
4385 {
4386 SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
4387 SCH_SHEET_PATH parentSheetPath = sheet;
4388
4389 parentSheetPath.push_back( parentSheet );
4390
4391 std::map<wxString, SCH_SHEET_PIN*> pins;
4392 std::map<wxString, SCH_HIERLABEL*> labels;
4393
4394 for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
4395 {
4396 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4397 pins[ pin->GetShownText( &parentSheetPath, false ) ] = pin;
4398
4399 if( pin->IsDangling() && settings.IsTestEnabled( ERCE_PIN_NOT_CONNECTED ) )
4400 {
4401 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
4402 ercItem->SetItems( pin );
4403 ercItem->SetSheetSpecificPath( sheet );
4404 ercItem->SetItemsSheetPaths( sheet );
4405
4406 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pin->GetPosition() );
4407 sheet.LastScreen()->Append( marker );
4408
4409 errors++;
4410 }
4411 }
4412
4413 if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
4414 {
4415 std::set<wxString> matchedPins;
4416
4417 for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
4418 {
4419 if( subItem->Type() == SCH_HIER_LABEL_T )
4420 {
4421 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
4422 wxString labelText = label->GetShownText( &parentSheetPath, false );
4423
4424 if( !pins.contains( labelText ) )
4425 labels[ labelText ] = label;
4426 else
4427 matchedPins.insert( labelText );
4428 }
4429 }
4430
4431 for( const wxString& matched : matchedPins )
4432 pins.erase( matched );
4433
4434 for( const auto& [name, pin] : pins )
4435 {
4436 msg.Printf( _( "Sheet pin %s has no matching hierarchical label inside the sheet" ),
4437 UnescapeString( name ) );
4438
4439 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4440 ercItem->SetItems( pin );
4441 ercItem->SetErrorMessage( msg );
4442 ercItem->SetSheetSpecificPath( sheet );
4443 ercItem->SetItemsSheetPaths( sheet );
4444
4445 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), pin->GetPosition() );
4446 sheet.LastScreen()->Append( marker );
4447
4448 errors++;
4449 }
4450
4451 for( const auto& [name, label] : labels )
4452 {
4453 msg.Printf( _( "Hierarchical label %s has no matching sheet pin in the parent sheet" ),
4454 UnescapeString( name ) );
4455
4456 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
4457 ercItem->SetItems( label );
4458 ercItem->SetErrorMessage( msg );
4459 ercItem->SetSheetSpecificPath( parentSheetPath );
4460 ercItem->SetItemsSheetPaths( parentSheetPath );
4461
4462 SCH_MARKER* marker = new SCH_MARKER( std::move( ercItem ), label->GetPosition() );
4463 parentSheet->GetScreen()->Append( marker );
4464
4465 errors++;
4466 }
4467 }
4468 }
4469 }
4470
4471 return errors;
4472}
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:530
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:600
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:561
void AddConnectionTo(const SCH_SHEET_PATH &aPath, SCH_ITEM *aItem)
Add a connection link between this item and another.
Definition sch_item.cpp:545
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:585
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.
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:376
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:233
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