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