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