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