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