KiCad PCB EDA Suite
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  * @author Jon Evans <jon@craftyjon.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <list>
22 #include <thread>
23 #include <algorithm>
24 #include <future>
25 #include <vector>
26 #include <unordered_map>
27 #include <profile.h>
28 #include <common.h>
29 #include <erc.h>
30 #include <sch_bus_entry.h>
31 #include <sch_component.h>
32 #include <sch_edit_frame.h>
33 #include <sch_line.h>
34 #include <sch_marker.h>
35 #include <sch_pin.h>
36 #include <sch_sheet.h>
37 #include <sch_sheet_path.h>
38 #include <sch_text.h>
39 #include <schematic.h>
40 #include <connection_graph.h>
41 #include <widgets/ui_common.h>
42 
43 #include <advanced_config.h> // for realtime connectivity switch
44 
45 
46 /*
47  * Flag to enable connectivity profiling
48  * @ingroup trace_env_vars
49  */
50 static const wxChar ConnProfileMask[] = wxT( "CONN_PROFILE" );
51 
52 
53 bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCreateMarkers )
54 {
55  PRIORITY highest_priority = PRIORITY::INVALID;
56  std::vector<SCH_ITEM*> candidates;
57  std::vector<SCH_ITEM*> strong_drivers;
58 
59  m_driver = nullptr;
60 
61  // Hierarchical labels are lower priority than local labels here,
62  // because on the first pass we want local labels to drive subgraphs
63  // so that we can identify same-sheet neighbors and link them together.
64  // Hierarchical labels will end up overriding the final net name if
65  // a higher-level sheet has a different name during the hierarchical
66  // pass.
67 
68  for( SCH_ITEM* item : m_drivers )
69  {
70  PRIORITY item_priority = GetDriverPriority( item );
71 
72  if( item_priority == PRIORITY::PIN
73  && !static_cast<SCH_PIN*>( item )->GetParentComponent()->IsInNetlist() )
74  continue;
75 
76  if( item_priority >= PRIORITY::HIER_LABEL )
77  strong_drivers.push_back( item );
78 
79  if( item_priority > highest_priority )
80  {
81  candidates.clear();
82  candidates.push_back( item );
83  highest_priority = item_priority;
84  }
85  else if( !candidates.empty() && ( item_priority == highest_priority ) )
86  {
87  candidates.push_back( item );
88  }
89  }
90 
91  if( highest_priority >= PRIORITY::HIER_LABEL )
92  m_strong_driver = true;
93 
94  // Power pins are 5, global labels are 6
95  m_local_driver = ( highest_priority < PRIORITY::POWER_PIN );
96 
97  if( !candidates.empty() )
98  {
99  if( candidates.size() > 1 )
100  {
101  if( highest_priority == PRIORITY::SHEET_PIN )
102  {
103  // We have multiple options, and they are all hierarchical
104  // sheet pins. Let's prefer outputs over inputs.
105 
106  for( auto c : candidates )
107  {
108  auto p = static_cast<SCH_SHEET_PIN*>( c );
109 
110  if( p->GetShape() == PINSHEETLABEL_SHAPE::PS_OUTPUT )
111  {
112  m_driver = c;
113  break;
114  }
115  }
116  }
117  else
118  {
119  // For all other driver types, sort by name
120  std::sort( candidates.begin(), candidates.end(),
121  [&]( SCH_ITEM* a, SCH_ITEM* b ) -> bool
122  {
123  SCH_CONNECTION* ac = a->Connection( m_sheet );
124  SCH_CONNECTION* bc = b->Connection( m_sheet );
125 
126  // Ensure we don't pick the subset over the superset
127  if( ac->IsBus() && bc->IsBus() )
128  return bc->IsSubsetOf( ac );
129 
130  return GetNameForDriver( a ) < GetNameForDriver( b );
131  } );
132  }
133  }
134 
135  if( !m_driver )
136  m_driver = candidates[0];
137  }
138 
139  if( strong_drivers.size() > 1 )
140  m_multiple_drivers = true;
141 
142  // Drop weak drivers
143  if( m_strong_driver )
144  m_drivers = strong_drivers;
145 
146  // Cache driver connection
147  if( m_driver )
149  else
150  m_driver_connection = nullptr;
151 
152  if( aCreateMarkers && m_multiple_drivers )
153  {
154  // First check if all the candidates are actually the same
155  bool same = true;
156  wxString first = GetNameForDriver( candidates[0] );
157  SCH_ITEM* second_item = nullptr;
158 
159  for( unsigned i = 1; i < candidates.size(); i++ )
160  {
161  if( GetNameForDriver( candidates[i] ) != first )
162  {
163  second_item = candidates[i];
164  same = false;
165  break;
166  }
167  }
168 
169  if( !same )
170  {
171  wxPoint pos = candidates[0]->Type() == SCH_PIN_T ?
172  static_cast<SCH_PIN*>( candidates[0] )->GetTransformedPosition() :
173  candidates[0]->GetPosition();
174 
175  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_DRIVER_CONFLICT );
176  ercItem->SetItems( candidates[0], second_item );
177 
178  SCH_MARKER* marker = new SCH_MARKER( ercItem, pos );
179  m_sheet.LastScreen()->Append( marker );
180 
181  // If aCreateMarkers is true, then this is part of ERC check, so we
182  // should return false even if the driver was assigned
183  return false;
184  }
185  }
186 
187  return aCreateMarkers || ( m_driver != nullptr );
188 }
189 
190 
192 {
193  if( !m_driver || m_dirty )
194  return "";
195 
196  if( !m_driver->Connection( m_sheet ) )
197  {
198 #ifdef CONNECTIVITY_DEBUG
199  wxASSERT_MSG( false, "Tried to get the net name of an item with no connection" );
200 #endif
201 
202  return "";
203  }
204 
205  return m_driver->Connection( m_sheet )->Name();
206 }
207 
208 
209 std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetBusLabels() const
210 {
211  std::vector<SCH_ITEM*> labels;
212 
213  for( SCH_ITEM* item : m_drivers )
214  {
215  switch( item->Type() )
216  {
217  case SCH_LABEL_T:
218  case SCH_GLOBAL_LABEL_T:
219  {
220  SCH_CONNECTION* label_conn = item->Connection( m_sheet );
221 
222  // Only consider bus vectors
223  if( label_conn->Type() == CONNECTION_TYPE::BUS )
224  labels.push_back( item );
225 
226  break;
227  }
228 
229  default: break;
230  }
231  }
232 
233  return labels;
234 }
235 
236 
238 {
239  if( m_driver_name_cache.count( aItem ) )
240  return m_driver_name_cache.at( aItem );
241 
242  switch( aItem->Type() )
243  {
244  case SCH_PIN_T:
245  {
246  SCH_PIN* pin = static_cast<SCH_PIN*>( aItem );
248  break;
249  }
250 
251  case SCH_LABEL_T:
252  case SCH_GLOBAL_LABEL_T:
253  case SCH_HIER_LABEL_T:
254  case SCH_SHEET_PIN_T:
255  {
256  m_driver_name_cache[aItem] = EscapeString( static_cast<SCH_TEXT*>( aItem )->GetShownText(),
257  CTX_NETNAME );
258  break;
259  }
260 
261  default:
262  wxFAIL_MSG( "Unhandled item type in GetNameForDriver" );
263  break;
264  }
265 
266  return m_driver_name_cache.at( aItem );
267 }
268 
269 
271 {
272  wxASSERT( m_sheet == aOther->m_sheet );
273 
274  for( SCH_ITEM* item : aOther->m_items )
275  {
277  AddItem( item );
278  }
279 
280  m_bus_neighbors.insert( aOther->m_bus_neighbors.begin(), aOther->m_bus_neighbors.end() );
281  m_bus_parents.insert( aOther->m_bus_parents.begin(), aOther->m_bus_parents.end() );
282 
284 
285  aOther->m_absorbed = true;
286  aOther->m_dirty = false;
287  aOther->m_driver = nullptr;
288  aOther->m_driver_connection = nullptr;
289  aOther->m_absorbed_by = this;
290 }
291 
292 
294 {
295  m_items.push_back( aItem );
296 
297  if( aItem->Connection( m_sheet )->IsDriver() )
298  m_drivers.push_back( aItem );
299 
300  if( aItem->Type() == SCH_SHEET_PIN_T )
301  m_hier_pins.push_back( static_cast<SCH_SHEET_PIN*>( aItem ) );
302  else if( aItem->Type() == SCH_HIER_LABEL_T )
303  m_hier_ports.push_back( static_cast<SCH_HIERLABEL*>( aItem ) );
304 }
305 
306 
308 {
309  if( !m_driver_connection )
310  return;
311 
312  for( SCH_ITEM* item : m_items )
313  {
314  SCH_CONNECTION* item_conn = item->Connection( m_sheet );
315 
316  if( !item_conn )
317  item_conn = item->InitializeConnection( m_sheet, m_graph );
318 
319  if( ( m_driver_connection->IsBus() && item_conn->IsNet() ) ||
320  ( m_driver_connection->IsNet() && item_conn->IsBus() ) )
321  {
322  continue;
323  }
324 
325  if( item != m_driver )
326  {
327  item_conn->Clone( *m_driver_connection );
328  item_conn->ClearDirty();
329  }
330  }
331 }
332 
333 
335 {
336  if( !aDriver )
337  return PRIORITY::NONE;
338 
339  switch( aDriver->Type() )
340  {
343  case SCH_LABEL_T: return PRIORITY::LOCAL_LABEL;
345  case SCH_PIN_T:
346  {
347  auto sch_pin = static_cast<SCH_PIN*>( aDriver );
348 
349  if( sch_pin->IsPowerConnection() )
350  return PRIORITY::POWER_PIN;
351  else
352  return PRIORITY::PIN;
353  }
354 
355  default: return PRIORITY::NONE;
356  }
357 }
358 
359 
361 
362 
364 {
365  for( auto& subgraph : m_subgraphs )
366  delete subgraph;
367 
368  m_items.clear();
369  m_subgraphs.clear();
370  m_driver_subgraphs.clear();
371  m_sheet_to_subgraphs_map.clear();
372  m_invisible_power_pins.clear();
373  m_bus_alias_cache.clear();
374  m_net_name_to_code_map.clear();
375  m_bus_name_to_code_map.clear();
378  m_item_to_subgraph_map.clear();
379  m_local_label_cache.clear();
380  m_global_label_cache.clear();
381  m_last_net_code = 1;
382  m_last_bus_code = 1;
384 }
385 
386 
387 void CONNECTION_GRAPH::Recalculate( const SCH_SHEET_LIST& aSheetList, bool aUnconditional )
388 {
389  PROF_COUNTER recalc_time( "CONNECTION_GRAPH::Recalculate" );
390 
391  if( aUnconditional )
392  Reset();
393 
394  PROF_COUNTER update_items( "updateItemConnectivity" );
395 
396  m_sheetList = aSheetList;
397 
398  for( const SCH_SHEET_PATH& sheet : aSheetList )
399  {
400  std::vector<SCH_ITEM*> items;
401 
402  for( SCH_ITEM* item : sheet.LastScreen()->Items() )
403  {
404  if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
405  items.push_back( item );
406  }
407 
408  m_items.reserve( m_items.size() + items.size() );
409 
410  updateItemConnectivity( sheet, items );
411 
412  // UpdateDanglingState() also adds connected items for SCH_TEXT
413  sheet.LastScreen()->TestDanglingEnds( &sheet );
414  }
415 
416  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
417  update_items.Show();
418 
419  PROF_COUNTER build_graph( "buildConnectionGraph" );
420 
422 
423  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
424  build_graph.Show();
425 
426  recalc_time.Stop();
427 
428  if( wxLog::IsAllowedTraceMask( ConnProfileMask ) )
429  recalc_time.Show();
430 
431 #ifndef DEBUG
432  // Pressure relief valve for release builds
433  const double max_recalc_time_msecs = 250.;
434 
435  if( m_allowRealTime && ADVANCED_CFG::GetCfg().m_realTimeConnectivity &&
436  recalc_time.msecs() > max_recalc_time_msecs )
437  {
438  m_allowRealTime = false;
439  }
440 #endif
441 }
442 
443 
445  const std::vector<SCH_ITEM*>& aItemList )
446 {
447  std::map< wxPoint, std::vector<SCH_ITEM*> > connection_map;
448 
449  for( SCH_ITEM* item : aItemList )
450  {
451  std::vector< wxPoint > points = item->GetConnectionPoints();
452  item->ConnectedItems( aSheet ).clear();
453 
454  if( item->Type() == SCH_SHEET_T )
455  {
456  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
457  {
458  if( !pin->Connection( aSheet ) )
459  pin->InitializeConnection( aSheet, this );
460 
461  pin->ConnectedItems( aSheet ).clear();
462  pin->Connection( aSheet )->Reset();
463 
464  connection_map[ pin->GetTextPos() ].push_back( pin );
465  m_items.emplace_back( pin );
466  }
467  }
468  else if( item->Type() == SCH_COMPONENT_T )
469  {
470  SCH_COMPONENT* component = static_cast<SCH_COMPONENT*>( item );
471 
472  // TODO(JE) right now this relies on GetPins() returning good SCH_PIN pointers
473  // that contain good LIB_PIN pointers. Since these get invalidated whenever the
474  // library component is refreshed, the current solution as of ed025972 is to just
475  // rebuild the SCH_PIN list when the component is refreshed, and then re-run the
476  // connectivity calculations. This is slow and should be improved before release.
477  // See https://gitlab.com/kicad/code/kicad/issues/3784
478 
479  for( SCH_PIN* pin : component->GetPins( &aSheet ) )
480  {
481  pin->InitializeConnection( aSheet, this );
482 
483  wxPoint pos = pin->GetPosition();
484 
485  // because calling the first time is not thread-safe
486  pin->GetDefaultNetName( aSheet );
487  pin->ConnectedItems( aSheet ).clear();
488 
489  // Invisible power pins need to be post-processed later
490 
491  if( pin->IsPowerConnection() && !pin->IsVisible() )
492  m_invisible_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
493 
494  connection_map[ pos ].push_back( pin );
495  m_items.emplace_back( pin );
496  }
497  }
498  else
499  {
500  m_items.emplace_back( item );
501  auto conn = item->InitializeConnection( aSheet, this );
502 
503  // Set bus/net property here so that the propagation code uses it
504  switch( item->Type() )
505  {
506  case SCH_LINE_T:
507  conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
509  break;
510 
511  case SCH_BUS_BUS_ENTRY_T:
512  conn->SetType( CONNECTION_TYPE::BUS );
513  // clean previous (old) links:
514  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
515  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
516  break;
517 
518  case SCH_PIN_T:
519  conn->SetType( CONNECTION_TYPE::NET );
520  break;
521 
523  conn->SetType( CONNECTION_TYPE::NET );
524  // clean previous (old) link:
525  static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
526  break;
527 
528  default:
529  break;
530  }
531 
532  for( const wxPoint& point : points )
533  connection_map[ point ].push_back( item );
534  }
535 
536  item->SetConnectivityDirty( false );
537  }
538 
539  for( const auto& it : connection_map )
540  {
541  auto connection_vec = it.second;
542 
543  for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ )
544  {
545  SCH_ITEM* connected_item = *primary_it;
546 
547  // Bus entries are special: they can have connection points in the
548  // middle of a wire segment, because the junction algo doesn't split
549  // the segment in two where you place a bus entry. This means that
550  // bus entries that don't land on the end of a line segment need to
551  // have "virtual" connection points to the segments they graphically
552  // touch.
553  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
554  {
555  // If this location only has the connection point of the bus
556  // entry itself, this means that either the bus entry is not
557  // connected to anything graphically, or that it is connected to
558  // a segment at some point other than at one of the endpoints.
559  if( connection_vec.size() == 1 )
560  {
561  SCH_SCREEN* screen = aSheet.LastScreen();
562  SCH_LINE* bus = screen->GetBus( it.first );
563 
564  if( bus )
565  {
566  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
567  bus_entry->m_connected_bus_item = bus;
568  }
569  }
570  }
571 
572  // Bus-to-bus entries are treated just like bus wires
573  else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
574  {
575  if( connection_vec.size() < 2 )
576  {
577  SCH_SCREEN* screen = aSheet.LastScreen();
578  SCH_LINE* bus = screen->GetBus( it.first );
579 
580  if( bus )
581  {
582  auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
583 
584  if( it.first == bus_entry->GetPosition() )
585  bus_entry->m_connected_bus_items[0] = bus;
586  else
587  bus_entry->m_connected_bus_items[1] = bus;
588 
589  bus_entry->ConnectedItems( aSheet ).insert( bus );
590  bus->ConnectedItems( aSheet ).insert( bus_entry );
591  }
592  }
593  }
594 
595  // Change junctions to be on bus junction layer if they are touching a bus
596  else if( connected_item->Type() == SCH_JUNCTION_T )
597  {
598  SCH_SCREEN* screen = aSheet.LastScreen();
599  SCH_LINE* bus = screen->GetBus( it.first );
600 
601  connected_item->SetLayer( bus ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
602  }
603 
604  for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ )
605  {
606  auto test_item = *test_it;
607 
608  if( connected_item != test_item &&
609  connected_item->ConnectionPropagatesTo( test_item ) &&
610  test_item->ConnectionPropagatesTo( connected_item ) )
611  {
612  connected_item->ConnectedItems( aSheet ).insert( test_item );
613  test_item->ConnectedItems( aSheet ).insert( connected_item );
614  }
615 
616  // Set up the link between the bus entry net and the bus
617  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
618  {
619  if( test_item->Connection( aSheet )->IsBus() )
620  {
621  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
622  bus_entry->m_connected_bus_item = test_item;
623  }
624  }
625  }
626 
627  // If we got this far and did not find a connected bus item for a bus entry,
628  // we should do a manual scan in case there is a bus item on this connection
629  // point but we didn't pick it up earlier because there is *also* a net item here.
630  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
631  {
632  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
633 
634  if( !bus_entry->m_connected_bus_item )
635  {
636  auto screen = aSheet.LastScreen();
637  auto bus = screen->GetBus( it.first );
638 
639  if( bus )
640  bus_entry->m_connected_bus_item = bus;
641  }
642  }
643  }
644  }
645 }
646 
647 
648 // TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
649 // to the same subgraph necessarily if it runs over and over again on the same
650 // sheet. We need:
651 //
652 // a) a cache of net/bus codes, like used before
653 // b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
654 // c) some way of trying to avoid changing net names. so we should keep track
655 // of the previous driver of a net, and if it comes down to choosing between
656 // equally-prioritized drivers, choose the one that already exists as a driver
657 // on some portion of the items.
658 
659 
661 {
662  // Recache all bus aliases for later use
663  wxCHECK_RET( m_schematic, "Connection graph cannot be built without schematic pointer" );
664 
665  SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
666 
667  for( unsigned i = 0; i < all_sheets.size(); i++ )
668  {
669  for( const auto& alias : all_sheets[i].LastScreen()->GetBusAliases() )
670  m_bus_alias_cache[ alias->GetName() ] = alias;
671  }
672 
673  // Build subgraphs from items (on a per-sheet basis)
674 
675  for( SCH_ITEM* item : m_items )
676  {
677  for( const auto& it : item->m_connection_map )
678  {
679  const auto sheet = it.first;
680  auto connection = it.second;
681 
682  if( connection->SubgraphCode() == 0 )
683  {
684  auto subgraph = new CONNECTION_SUBGRAPH( this );
685 
686  subgraph->m_code = m_last_subgraph_code++;
687  subgraph->m_sheet = sheet;
688 
689  subgraph->AddItem( item );
690 
691  connection->SetSubgraphCode( subgraph->m_code );
692  m_item_to_subgraph_map[item] = subgraph;
693 
694  std::list<SCH_ITEM*> members;
695 
696  auto get_items =
697  [&]( SCH_ITEM* aItem ) -> bool
698  {
699  auto* conn = aItem->Connection( sheet );
700 
701  if( !conn )
702  conn = aItem->InitializeConnection( sheet, this );
703 
704  return ( conn->SubgraphCode() == 0 );
705  };
706 
707  std::copy_if( item->ConnectedItems( sheet ).begin(),
708  item->ConnectedItems( sheet ).end(),
709  std::back_inserter( members ), get_items );
710 
711  for( auto connected_item : members )
712  {
713  if( connected_item->Type() == SCH_NO_CONNECT_T )
714  subgraph->m_no_connect = connected_item;
715 
716  auto connected_conn = connected_item->Connection( sheet );
717 
718  wxASSERT( connected_conn );
719 
720  if( connected_conn->SubgraphCode() == 0 )
721  {
722  connected_conn->SetSubgraphCode( subgraph->m_code );
723  m_item_to_subgraph_map[connected_item] = subgraph;
724  subgraph->AddItem( connected_item );
725 
726  std::copy_if( connected_item->ConnectedItems( sheet ).begin(),
727  connected_item->ConnectedItems( sheet ).end(),
728  std::back_inserter( members ), get_items );
729  }
730  }
731 
732  subgraph->m_dirty = true;
733  m_subgraphs.push_back( subgraph );
734  }
735  }
736  }
737 
758  // Resolve drivers for subgraphs and propagate connectivity info
759 
760  // We don't want to spin up a new thread for fewer than 8 nets (overhead costs)
761  size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
762  ( m_subgraphs.size() + 3 ) / 4 );
763 
764  std::atomic<size_t> nextSubgraph( 0 );
765  std::vector<std::future<size_t>> returns( parallelThreadCount );
766  std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
767 
768  std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
769  [&] ( const CONNECTION_SUBGRAPH* candidate )
770  {
771  return candidate->m_dirty;
772  } );
773 
774  auto update_lambda = [&nextSubgraph, &dirty_graphs]() -> size_t
775  {
776  for( size_t subgraphId = nextSubgraph++; subgraphId < dirty_graphs.size(); subgraphId = nextSubgraph++ )
777  {
778  auto subgraph = dirty_graphs[subgraphId];
779 
780  if( !subgraph->m_dirty )
781  continue;
782 
783  // Special processing for some items
784  for( auto item : subgraph->m_items )
785  {
786  switch( item->Type() )
787  {
788  case SCH_NO_CONNECT_T:
789  subgraph->m_no_connect = item;
790  break;
791 
793  subgraph->m_bus_entry = item;
794  break;
795 
796  case SCH_PIN_T:
797  {
798  auto pin = static_cast<SCH_PIN*>( item );
799 
800  if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
801  subgraph->m_no_connect = item;
802 
803  break;
804  }
805 
806  default:
807  break;
808  }
809  }
810 
811  if( !subgraph->ResolveDrivers() )
812  {
813  subgraph->m_dirty = false;
814  }
815  else
816  {
817  // Now the subgraph has only one driver
818  SCH_ITEM* driver = subgraph->m_driver;
819  SCH_SHEET_PATH sheet = subgraph->m_sheet;
820  SCH_CONNECTION* connection = driver->Connection( sheet );
821 
822  connection->ConfigureFromLabel( subgraph->GetNameForDriver( driver ) );
823  connection->SetDriver( driver );
824  connection->ClearDirty();
825 
826  subgraph->m_dirty = false;
827  }
828  }
829 
830  return 1;
831  };
832 
833  if( parallelThreadCount == 1 )
834  update_lambda();
835  else
836  {
837  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
838  returns[ii] = std::async( std::launch::async, update_lambda );
839 
840  // Finalize the threads
841  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
842  returns[ii].wait();
843  }
844 
845  // Now discard any non-driven subgraphs from further consideration
846 
847  std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
848  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
849  {
850  return candidate->m_driver;
851  } );
852 
853  // Check for subgraphs with the same net name but only weak drivers.
854  // For example, two wires that are both connected to hierarchical
855  // sheet pins that happen to have the same name, but are not the same.
856 
857  for( auto&& subgraph : m_driver_subgraphs )
858  {
859  wxString full_name = subgraph->m_driver_connection->Name();
860  wxString name = subgraph->m_driver_connection->Name( true );
861  m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
862 
863  subgraph->m_dirty = true;
864 
865  if( subgraph->m_strong_driver )
866  {
867  SCH_ITEM* driver = subgraph->m_driver;
868  SCH_SHEET_PATH sheet = subgraph->m_sheet;
869 
870  switch( driver->Type() )
871  {
872  case SCH_LABEL_T:
873  case SCH_HIER_LABEL_T:
874  {
875  m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
876  break;
877  }
878  case SCH_GLOBAL_LABEL_T:
879  {
880  m_global_label_cache[name].push_back( subgraph );
881  break;
882  }
883  case SCH_PIN_T:
884  {
885  auto pin = static_cast<SCH_PIN*>( driver );
886  wxASSERT( pin->IsPowerConnection() );
887  m_global_label_cache[name].push_back( subgraph );
888  break;
889  }
890  default:
891  wxLogTrace( "CONN", "Unexpected strong driver %s",
893  break;
894  }
895  }
896  }
897 
898  // Generate subgraphs for invisible power pins. These will be merged with other subgraphs
899  // on the same sheet in the next loop.
900 
901  std::unordered_map<int, CONNECTION_SUBGRAPH*> invisible_pin_subgraphs;
902 
903  for( const auto& it : m_invisible_power_pins )
904  {
905  SCH_SHEET_PATH sheet = it.first;
906  SCH_PIN* pin = it.second;
907 
908  if( !pin->ConnectedItems( sheet ).empty() && !pin->GetLibPin()->GetParent()->IsPower() )
909  {
910  // ERC will warn about this: user has wired up an invisible pin
911  continue;
912  }
913 
914  SCH_CONNECTION* connection = pin->Connection( sheet );
915 
916  if( !connection )
917  connection = pin->InitializeConnection( sheet, this );
918 
919  // If this pin already has a subgraph, don't need to process
920  if( connection->SubgraphCode() > 0 )
921  continue;
922 
923  connection->SetName( pin->GetName() );
924 
925  int code = assignNewNetCode( *connection );
926 
927  connection->SetNetCode( code );
928 
929  CONNECTION_SUBGRAPH* subgraph;
930 
931  if( invisible_pin_subgraphs.count( code ) )
932  {
933  subgraph = invisible_pin_subgraphs.at( code );
934  subgraph->AddItem( pin );
935  }
936  else
937  {
938  subgraph = new CONNECTION_SUBGRAPH( this );
939 
940  subgraph->m_code = m_last_subgraph_code++;
941  subgraph->m_sheet = sheet;
942 
943  subgraph->AddItem( pin );
944  subgraph->ResolveDrivers();
945 
946  auto key = std::make_pair( subgraph->GetNetName(), code );
947  m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
948  m_subgraphs.push_back( subgraph );
949  m_driver_subgraphs.push_back( subgraph );
950 
951  invisible_pin_subgraphs[code] = subgraph;
952  }
953 
954  connection->SetSubgraphCode( subgraph->m_code );
955  }
956 
957  for( auto it : invisible_pin_subgraphs )
958  it.second->UpdateItemConnections();
959 
960  // Here we do all the local (sheet) processing of each subgraph, including assigning net
961  // codes, merging subgraphs together that use label connections, etc.
962 
963  // Cache remaining valid subgraphs by sheet path
964  for( auto subgraph : m_driver_subgraphs )
965  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
966 
967  std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
968 
969  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
970  {
971  if( subgraph->m_absorbed )
972  continue;
973 
974  SCH_CONNECTION* connection = subgraph->m_driver_connection;
975  SCH_SHEET_PATH sheet = subgraph->m_sheet;
976  wxString name = connection->Name();
977 
978  // Test subgraphs with weak drivers for net name conflicts and fix them
979  unsigned suffix = 1;
980 
981  auto create_new_name =
982  [&suffix]( SCH_CONNECTION* aConn ) -> wxString
983  {
984  wxString newName;
985 
986  // For group buses with a prefix, we can add the suffix to the prefix.
987  // If they don't have a prefix, we force the creation of a prefix so that
988  // two buses don't get inadvertently shorted together.
989  if( aConn->Type() == CONNECTION_TYPE::BUS_GROUP )
990  {
991  wxString prefix = aConn->BusPrefix();
992 
993  if( prefix.empty() )
994  prefix = wxT( "BUS" ); // So result will be "BUS_1{...}"
995 
996  wxString oldName = aConn->Name().AfterFirst( '{' );
997 
998  newName = wxString::Format( "%s_%u{%s", prefix, suffix, oldName );
999 
1000  aConn->ConfigureFromLabel( newName );
1001  }
1002  else
1003  {
1004  newName = wxString::Format( "%s_%u", aConn->Name(), suffix );
1005  aConn->SetSuffix( wxString::Format( "_%u", suffix ) );
1006  }
1007 
1008  suffix++;
1009  return newName;
1010  };
1011 
1012  if( !subgraph->m_strong_driver )
1013  {
1014  auto& vec = m_net_name_to_subgraphs_map.at( name );
1015 
1016  if( vec.size() > 1 )
1017  {
1018  wxString new_name = create_new_name( connection );
1019 
1020  while( m_net_name_to_subgraphs_map.count( new_name ) )
1021  new_name = create_new_name( connection );
1022 
1023  wxLogTrace( "CONN", "%ld (%s) is weakly driven and not unique. Changing to %s.",
1024  subgraph->m_code, name, new_name );
1025 
1026  vec.erase( std::remove( vec.begin(), vec.end(), subgraph ), vec.end() );
1027 
1028  m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1029 
1030  name = new_name;
1031 
1032  subgraph->UpdateItemConnections();
1033  }
1034  else
1035  {
1036  // If there is no conflict, promote sheet pins to be strong drivers so that they
1037  // will be considered below for propagation/merging.
1038 
1039  // It is possible for this to generate a conflict if the sheet pin has the same
1040  // name as a global label on the same sheet, because global merging will then treat
1041  // this subgraph as if it had a matching local label. So, for those cases, we
1042  // don't apply this promotion
1043 
1044  if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1045  {
1046  bool conflict = false;
1047  wxString global_name = connection->Name( true );
1048  size_t count = m_net_name_to_subgraphs_map.count( global_name );
1049 
1050  if( count )
1051  {
1052  // A global will conflict if it is on the same sheet as this subgraph, since
1053  // it would be connected by implicit local label linking
1054  auto& candidates = m_net_name_to_subgraphs_map.at( global_name );
1055 
1056  for( const auto& candidate : candidates )
1057  {
1058  if( candidate->m_sheet == sheet )
1059  conflict = true;
1060  }
1061  }
1062 
1063  if( conflict )
1064  {
1065  wxLogTrace( "CONN",
1066  "%ld (%s) skipped for promotion due to potential conflict",
1067  subgraph->m_code, name );
1068  }
1069  else
1070  {
1071  wxLogTrace( "CONN",
1072  "%ld (%s) weakly driven by unique sheet pin %s, promoting",
1073  subgraph->m_code, name,
1074  subgraph->m_driver->GetSelectMenuText( EDA_UNITS::MILLIMETRES ) );
1075 
1076  subgraph->m_strong_driver = true;
1077  }
1078  }
1079  }
1080  }
1081 
1082  // Assign net codes
1083 
1084  if( connection->IsBus() )
1085  {
1086  int code = -1;
1087 
1088  if( m_bus_name_to_code_map.count( name ) )
1089  {
1090  code = m_bus_name_to_code_map.at( name );
1091  }
1092  else
1093  {
1094  code = m_last_bus_code++;
1095  m_bus_name_to_code_map[ name ] = code;
1096  }
1097 
1098  connection->SetBusCode( code );
1099  assignNetCodesToBus( connection );
1100  }
1101  else
1102  {
1103  assignNewNetCode( *connection );
1104  }
1105 
1106  subgraph->UpdateItemConnections();
1107 
1108  // Reset the flag for the next loop below
1109  subgraph->m_dirty = true;
1110 
1111  // Next, we merge together subgraphs that have label connections, and create
1112  // neighbor links for subgraphs that are part of a bus on the same sheet.
1113  // For merging, we consider each possible strong driver.
1114 
1115  // If this subgraph doesn't have a strong driver, let's skip it, since there is no
1116  // way it will be merged with anything.
1117 
1118  if( !subgraph->m_strong_driver )
1119  continue;
1120 
1121  // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
1122  // as the subgraph we are considering that has a strong driver.
1123  // Weakly driven subgraphs are not considered since they will never be absorbed or
1124  // form neighbor links.
1125 
1126  std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
1127  std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
1128  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
1129  std::back_inserter( candidate_subgraphs ),
1130  [&] ( const CONNECTION_SUBGRAPH* candidate )
1131  {
1132  return ( !candidate->m_absorbed &&
1133  candidate->m_strong_driver &&
1134  candidate != subgraph );
1135  } );
1136 
1137  // This is a list of connections on the current subgraph to compare to the
1138  // drivers of each candidate subgraph. If the current subgraph is a bus,
1139  // we should consider each bus member.
1140  std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
1141 
1142  // Also check the main driving connection
1143  connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
1144 
1145  auto add_connections_to_check = [&] ( CONNECTION_SUBGRAPH* aSubgraph ) {
1146  for( SCH_ITEM* possible_driver : aSubgraph->m_items )
1147  {
1148  if( possible_driver == aSubgraph->m_driver )
1149  continue;
1150 
1151  auto c = getDefaultConnection( possible_driver, aSubgraph );
1152 
1153  if( c )
1154  {
1155  if( c->Type() != aSubgraph->m_driver_connection->Type() )
1156  continue;
1157 
1158  if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
1159  continue;
1160 
1161  connections_to_check.push_back( c );
1162  wxLogTrace( "CONN", "%lu (%s): Adding secondary driver %s", aSubgraph->m_code,
1163  aSubgraph->m_driver_connection->Name( true ), c->Name( true ) );
1164  }
1165  }
1166  };
1167 
1168  // Now add other strong drivers
1169  // The actual connection attached to these items will have been overwritten
1170  // by the chosen driver of the subgraph, so we need to create a dummy connection
1171  add_connections_to_check( subgraph );
1172 
1173  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1174  {
1175  auto member = connections_to_check[i];
1176 
1177  if( member->IsBus() )
1178  {
1179  connections_to_check.insert( connections_to_check.end(),
1180  member->Members().begin(),
1181  member->Members().end() );
1182  }
1183 
1184  wxString test_name = member->Name( true );
1185 
1186  for( auto candidate : candidate_subgraphs )
1187  {
1188  if( candidate->m_absorbed )
1189  continue;
1190 
1191  bool match = false;
1192 
1193  if( candidate->m_driver_connection->Name( true ) == test_name )
1194  {
1195  match = true;
1196  }
1197  else
1198  {
1199  if( !candidate->m_multiple_drivers )
1200  continue;
1201 
1202  for( SCH_ITEM *driver : candidate->m_drivers )
1203  {
1204  if( driver == candidate->m_driver )
1205  continue;
1206 
1207  // Sheet pins are not candidates for merging
1208  if( driver->Type() == SCH_SHEET_PIN_T )
1209  continue;
1210 
1211  if( driver->Type() == SCH_PIN_T )
1212  {
1213  auto pin = static_cast<SCH_PIN*>( driver );
1214 
1215  if( pin->IsPowerConnection() && pin->GetName() == test_name )
1216  {
1217  match = true;
1218  break;
1219  }
1220  }
1221  else
1222  {
1223  wxASSERT( driver->Type() == SCH_LABEL_T ||
1224  driver->Type() == SCH_GLOBAL_LABEL_T ||
1225  driver->Type() == SCH_HIER_LABEL_T );
1226 
1227  if( subgraph->GetNameForDriver( driver ) == test_name )
1228  {
1229  match = true;
1230  break;
1231  }
1232  }
1233  }
1234  }
1235 
1236  if( match )
1237  {
1238  if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
1239  {
1240  wxLogTrace( "CONN", "%lu (%s) has bus child %lu (%s)", subgraph->m_code,
1241  connection->Name(), candidate->m_code, member->Name() );
1242 
1243  subgraph->m_bus_neighbors[member].insert( candidate );
1244  candidate->m_bus_parents[member].insert( subgraph );
1245  }
1246  else
1247  {
1248  wxLogTrace( "CONN", "%lu (%s) absorbs neighbor %lu (%s)",
1249  subgraph->m_code, connection->Name(),
1250  candidate->m_code, candidate->m_driver_connection->Name() );
1251 
1252  // Candidate may have other non-chosen drivers we need to follow
1253  add_connections_to_check( candidate );
1254 
1255  subgraph->Absorb( candidate );
1256  invalidated_subgraphs.insert( subgraph );
1257  }
1258  }
1259  }
1260  }
1261  }
1262 
1263  // Update any subgraph that was invalidated above
1264  for( auto subgraph : invalidated_subgraphs )
1265  {
1266  if( subgraph->m_absorbed )
1267  continue;
1268 
1269  subgraph->ResolveDrivers();
1270 
1271  if( subgraph->m_driver_connection->IsBus() )
1272  assignNetCodesToBus( subgraph->m_driver_connection );
1273  else
1274  assignNewNetCode( *subgraph->m_driver_connection );
1275 
1276  subgraph->UpdateItemConnections();
1277 
1278  wxLogTrace( "CONN", "Re-resolving drivers for %lu (%s)", subgraph->m_code,
1279  subgraph->m_driver_connection->Name() );
1280  }
1281 
1282  // Absorbed subgraphs should no longer be considered
1283  m_driver_subgraphs.erase( std::remove_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1284  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1285  {
1286  return candidate->m_absorbed;
1287  } ),
1288  m_driver_subgraphs.end() );
1289 
1290  // Store global subgraphs for later reference
1291  std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
1292  std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1293  std::back_inserter( global_subgraphs ),
1294  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1295  {
1296  return !candidate->m_local_driver;
1297  } );
1298 
1299  // Recache remaining valid subgraphs by sheet path
1300  m_sheet_to_subgraphs_map.clear();
1301  for( auto subgraph : m_driver_subgraphs )
1302  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1303 
1304  // Next time through the subgraphs, we do some post-processing to handle things like
1305  // connecting bus members to their neighboring subgraphs, and then propagate connections
1306  // through the hierarchy
1307 
1308  for( auto subgraph : m_driver_subgraphs )
1309  {
1310  if( !subgraph->m_dirty )
1311  continue;
1312 
1313  // For subgraphs that are driven by a global (power port or label) and have more
1314  // than one global driver, we need to seek out other subgraphs driven by the
1315  // same name as the non-chosen driver and update them to match the chosen one.
1316 
1317  if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
1318  {
1319  for( SCH_ITEM* driver : subgraph->m_drivers )
1320  {
1321  if( driver == subgraph->m_driver )
1322  continue;
1323 
1324  wxString secondary_name = subgraph->GetNameForDriver( driver );
1325 
1326  if( secondary_name == subgraph->m_driver_connection->Name() )
1327  continue;
1328 
1329  bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
1331 
1332  for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
1333  {
1334  if( candidate == subgraph )
1335  continue;
1336 
1337  if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
1338  continue;
1339 
1340  SCH_CONNECTION* conn = candidate->m_driver_connection;
1341 
1342  if( conn->Name() == secondary_name )
1343  {
1344  wxLogTrace( "CONN", "Global %lu (%s) promoted to %s", candidate->m_code,
1345  conn->Name(), subgraph->m_driver_connection->Name() );
1346 
1347  conn->Clone( *subgraph->m_driver_connection );
1348  candidate->UpdateItemConnections();
1349 
1350  candidate->m_dirty = false;
1351  }
1352  }
1353  }
1354  }
1355 
1356  // This call will handle descending the hierarchy and updating child subgraphs
1357  propagateToNeighbors( subgraph );
1358  }
1359 
1360  // Handle buses that have been linked together somewhere by member (net) connections.
1361  // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
1362 
1363  // For net subgraphs that have more than one bus parent, we need to ensure that those
1364  // buses are linked together in the final netlist. The final name of each bus might not
1365  // match the local name that was used to establish the parent-child relationship, because
1366  // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
1367  // we need to identify the appropriate bus members to link together (and their final names),
1368  // and then update all instances of the old name in the hierarchy.
1369 
1370  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1371  {
1372  if( subgraph->m_bus_parents.size() < 2 )
1373  continue;
1374 
1375  SCH_CONNECTION* conn = subgraph->m_driver_connection;
1376 
1377  wxLogTrace( "CONN", "%lu (%s) has multiple bus parents", subgraph->m_code, conn->Name() );
1378 
1379  wxASSERT( conn->IsNet() );
1380 
1381  for( const auto& it : subgraph->m_bus_parents )
1382  {
1383  SCH_CONNECTION* link_member = it.first.get();
1384 
1385  for( CONNECTION_SUBGRAPH* parent : it.second )
1386  {
1387  while( parent->m_absorbed )
1388  parent = parent->m_absorbed_by;
1389 
1390  SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
1391 
1392  if( !match )
1393  {
1394  wxLogTrace( "CONN", "Warning: could not match %s inside %lu (%s)", conn->Name(),
1395  parent->m_code, parent->m_driver_connection->Name() );
1396  continue;
1397  }
1398 
1399  if( conn->Name() != match->Name() )
1400  {
1401  wxString old_name = match->Name();
1402 
1403  wxLogTrace( "CONN", "Updating %lu (%s) member %s to %s", parent->m_code,
1404  parent->m_driver_connection->Name(), old_name, conn->Name() );
1405 
1406  match->Clone( *conn );
1407 
1408  if( !m_net_name_to_subgraphs_map.count( old_name ) )
1409  continue;
1410 
1411  for( CONNECTION_SUBGRAPH* old_sg : m_net_name_to_subgraphs_map.at( old_name ) )
1412  {
1413  while( old_sg->m_absorbed )
1414  old_sg = old_sg->m_absorbed_by;
1415 
1416  old_sg->m_driver_connection->Clone( *conn );
1417  old_sg->UpdateItemConnections();
1418  }
1419  }
1420  }
1421  }
1422  }
1423 
1426 
1427  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1428  {
1429  // Every driven subgraph should have been marked by now
1430  if( subgraph->m_dirty )
1431  {
1432  // TODO(JE) this should be caught by hierarchical sheet port/pin ERC, check this
1433  // Reset to false so no complaints come up later
1434  subgraph->m_dirty = false;
1435  }
1436 
1437  if( subgraph->m_driver_connection->IsBus() )
1438  {
1439  // No other processing to do on buses
1440  continue;
1441  }
1442  else
1443  {
1444  // As a visual aid, we can check sheet pins that are driven by themselves to see
1445  // if they should be promoted to buses
1446 
1447  if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1448  {
1449  SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
1450 
1451  if( SCH_SHEET* sheet = pin->GetParent() )
1452  {
1453  wxString pinText = pin->GetText();
1454 
1455  for( auto item : sheet->GetScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
1456  {
1457  auto label = static_cast<SCH_HIERLABEL*>( item );
1458 
1459  if( label->GetText() == pinText )
1460  {
1461  SCH_SHEET_PATH path = subgraph->m_sheet;
1462  path.push_back( sheet );
1463 
1464  SCH_CONNECTION* parent_conn = label->Connection( path );
1465 
1466  if( parent_conn && parent_conn->IsBus() )
1467  subgraph->m_driver_connection->SetType( CONNECTION_TYPE::BUS );
1468 
1469  break;
1470  }
1471  }
1472 
1473  if( subgraph->m_driver_connection->IsBus() )
1474  continue;
1475  }
1476  }
1477  }
1478 
1479  auto key = std::make_pair( subgraph->GetNetName(),
1480  subgraph->m_driver_connection->NetCode() );
1481  m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1482 
1483  m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
1484  }
1485 
1486  // Clean up and deallocate stale subgraphs
1487  m_subgraphs.erase( std::remove_if( m_subgraphs.begin(), m_subgraphs.end(),
1488  [&]( const CONNECTION_SUBGRAPH* sg )
1489  {
1490  if( sg->m_absorbed )
1491  {
1492  delete sg;
1493  return true;
1494  }
1495  else
1496  {
1497  return false;
1498  }
1499  } ),
1500  m_subgraphs.end() );
1501 }
1502 
1503 
1505 {
1506  int code;
1507 
1508  if( m_net_name_to_code_map.count( aConnection.Name() ) )
1509  {
1510  code = m_net_name_to_code_map.at( aConnection.Name() );
1511  }
1512  else
1513  {
1514  code = m_last_net_code++;
1515  m_net_name_to_code_map[ aConnection.Name() ] = code;
1516  }
1517 
1518  aConnection.SetNetCode( code );
1519 
1520  return code;
1521 }
1522 
1523 
1525 {
1526  auto connections_to_check( aConnection->Members() );
1527 
1528  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1529  {
1530  auto member = connections_to_check[i];
1531 
1532  if( member->IsBus() )
1533  {
1534  connections_to_check.insert( connections_to_check.end(),
1535  member->Members().begin(),
1536  member->Members().end() );
1537  continue;
1538  }
1539 
1540  assignNewNetCode( *member );
1541  }
1542 }
1543 
1544 
1546 {
1547  SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
1548  std::vector<CONNECTION_SUBGRAPH*> search_list;
1549  std::unordered_set<CONNECTION_SUBGRAPH*> visited;
1550  std::vector<SCH_CONNECTION*> stale_bus_members;
1551 
1552  auto visit = [&] ( CONNECTION_SUBGRAPH* aParent ) {
1553  for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
1554  {
1555  SCH_SHEET_PATH path = aParent->m_sheet;
1556  path.push_back( pin->GetParent() );
1557 
1558  if( !m_sheet_to_subgraphs_map.count( path ) )
1559  continue;
1560 
1561  for( CONNECTION_SUBGRAPH* candidate : m_sheet_to_subgraphs_map.at( path ) )
1562  {
1563  if( !candidate->m_strong_driver ||
1564  candidate->m_hier_ports.empty() ||
1565  visited.count( candidate ) )
1566  continue;
1567 
1568  for( SCH_HIERLABEL* label : candidate->m_hier_ports )
1569  {
1570  if( candidate->GetNameForDriver( label ) == aParent->GetNameForDriver( pin ) )
1571  {
1572  wxLogTrace( "CONN", "%lu: found child %lu (%s)", aParent->m_code,
1573  candidate->m_code, candidate->m_driver_connection->Name() );
1574 
1575  candidate->m_hier_parent = aParent;
1576 
1577  search_list.push_back( candidate );
1578  break;
1579  }
1580  }
1581  }
1582  }
1583 
1584  for( SCH_HIERLABEL* label : aParent->m_hier_ports )
1585  {
1586  SCH_SHEET_PATH path = aParent->m_sheet;
1587  path.pop_back();
1588 
1589  if( !m_sheet_to_subgraphs_map.count( path ) )
1590  continue;
1591 
1592  for( CONNECTION_SUBGRAPH* candidate : m_sheet_to_subgraphs_map.at( path ) )
1593  {
1594  if( candidate->m_hier_pins.empty() ||
1595  visited.count( candidate ) ||
1596  ( candidate->m_driver_connection->Type() !=
1597  aParent->m_driver_connection->Type() ) )
1598  continue;
1599 
1600  for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
1601  {
1602  SCH_SHEET_PATH pin_path = path;
1603  pin_path.push_back( pin->GetParent() );
1604 
1605  if( pin_path != aParent->m_sheet )
1606  continue;
1607 
1608  if( aParent->GetNameForDriver( label ) == candidate->GetNameForDriver( pin ) )
1609  {
1610  wxLogTrace( "CONN", "%lu: found additional parent %lu (%s)",
1611  aParent->m_code, candidate->m_code,
1612  candidate->m_driver_connection->Name() );
1613 
1614  search_list.push_back( candidate );
1615  break;
1616  }
1617  }
1618  }
1619  }
1620  };
1621 
1622  auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
1623  for( const auto& kv : aParentGraph->m_bus_neighbors )
1624  {
1625  for( CONNECTION_SUBGRAPH* neighbor : kv.second )
1626  {
1627  // May have been absorbed but won't have been deleted
1628  while( neighbor->m_absorbed )
1629  neighbor = neighbor->m_absorbed_by;
1630 
1631  SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
1632 
1633  // Now member may be out of date, since we just cloned the
1634  // connection from higher up in the hierarchy. We need to
1635  // figure out what the actual new connection is.
1636  SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
1637 
1638  if( !member )
1639  {
1640  // Try harder: we might match on a secondary driver
1641  for( CONNECTION_SUBGRAPH* sg : kv.second )
1642  {
1643  if( sg->m_multiple_drivers )
1644  {
1645  SCH_SHEET_PATH sheet = sg->m_sheet;
1646 
1647  for( SCH_ITEM* driver : sg->m_drivers )
1648  {
1649  auto c = getDefaultConnection( driver, sg );
1650  member = matchBusMember( parent, c.get() );
1651 
1652  if( member )
1653  break;
1654  }
1655  }
1656 
1657  if( member )
1658  break;
1659  }
1660  }
1661 
1662  // This is bad, probably an ERC error
1663  if( !member )
1664  {
1665  wxLogTrace( "CONN", "Could not match bus member %s in %s",
1666  kv.first->Name(), parent->Name() );
1667  continue;
1668  }
1669 
1670  auto neighbor_conn = neighbor->m_driver_connection;
1671  auto neighbor_name = neighbor_conn->Name();
1672 
1673  // Matching name: no update needed
1674  if( neighbor_name == member->Name() )
1675  continue;
1676 
1677  // Safety check against infinite recursion
1678  wxASSERT( neighbor_conn->IsNet() );
1679 
1680  wxLogTrace( "CONN", "%lu (%s) connected to bus member %s (local %s)",
1681  neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
1682 
1683  // Take whichever name is higher priority
1686  {
1687  member->Clone( *neighbor_conn );
1688  stale_bus_members.push_back( member );
1689  }
1690  else
1691  {
1692  neighbor_conn->Clone( *member );
1693  neighbor->UpdateItemConnections();
1694 
1695  recacheSubgraphName( neighbor, neighbor_name );
1696 
1697  // Recurse onto this neighbor in case it needs to re-propagate
1698  neighbor->m_dirty = true;
1699  propagateToNeighbors( neighbor );
1700  }
1701  }
1702  }
1703  };
1704 
1705  // If we are a bus, we must propagate to local neighbors and then the hierarchy
1706  if( conn->IsBus() )
1707  propagate_bus_neighbors( aSubgraph );
1708 
1709  // If we don't have any hier pins (i.e. no children), nothing to do
1710  if( aSubgraph->m_hier_pins.empty() )
1711  {
1712  // If we also don't have any parents, we'll never be visited again
1713  if( aSubgraph->m_hier_ports.empty() )
1714  aSubgraph->m_dirty = false;
1715 
1716  return;
1717  }
1718 
1719  // If we do have hier ports, skip this subgraph as it will be visited by a parent
1720  // TODO(JE) this will leave the subgraph dirty if there is no matching parent subgraph,
1721  // which should be flagged as an ERC error
1722  if( !aSubgraph->m_hier_ports.empty() )
1723  return;
1724 
1725  visited.insert( aSubgraph );
1726 
1727  wxLogTrace( "CONN", "Propagating %lu (%s) to subsheets",
1728  aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
1729 
1730  visit( aSubgraph );
1731 
1732  for( unsigned i = 0; i < search_list.size(); i++ )
1733  {
1734  auto child = search_list[i];
1735 
1736  visited.insert( child );
1737 
1738  visit( child );
1739 
1740  child->m_dirty = false;
1741  }
1742 
1743  // Now, find the best driver for this chain of subgraphs
1744  CONNECTION_SUBGRAPH* driver = aSubgraph;
1747 
1748  // Check if a subsheet has a higher-priority connection to the same net
1750  {
1751  for( CONNECTION_SUBGRAPH* subgraph : visited )
1752  {
1754  CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
1755 
1756  // Upgrade driver to be this subgraph if this subgraph has a power pin or global
1757  // Also upgrade if we found something with a shorter sheet path (higher in hierarchy)
1758  // but with an equivalent priority
1759 
1760  if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
1761  ( priority >= highest && subgraph->m_sheet.size() < aSubgraph->m_sheet.size() ) )
1762  driver = subgraph;
1763  }
1764  }
1765 
1766  if( driver != aSubgraph )
1767  {
1768  wxLogTrace( "CONN", "%lu (%s) overridden by new driver %lu (%s)",
1769  aSubgraph->m_code, aSubgraph->m_driver_connection->Name(),
1770  driver->m_code, driver->m_driver_connection->Name() );
1771  }
1772 
1773  conn = driver->m_driver_connection;
1774 
1775  for( CONNECTION_SUBGRAPH* subgraph : visited )
1776  {
1777  wxString old_name = subgraph->m_driver_connection->Name();
1778 
1779  subgraph->m_driver_connection->Clone( *conn );
1780  subgraph->UpdateItemConnections();
1781 
1782  if( old_name != conn->Name() )
1783  recacheSubgraphName( subgraph, old_name );
1784 
1785  if( conn->IsBus() )
1786  propagate_bus_neighbors( subgraph );
1787  }
1788 
1789  // Somewhere along the way, a bus member may have been upgraded to a global or power label.
1790  // Because this can happen anywhere, we need a second pass to update all instances of that bus
1791  // member to have the correct connection info
1792  if( conn->IsBus() && !stale_bus_members.empty() )
1793  {
1794  for( auto stale_member : stale_bus_members )
1795  {
1796  for( CONNECTION_SUBGRAPH* subgraph : visited )
1797  {
1798  SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
1799  stale_member );
1800  wxASSERT( member );
1801 
1802  wxLogTrace( "CONN", "Updating %lu (%s) member %s to %s", subgraph->m_code,
1803  subgraph->m_driver_connection->Name(), member->LocalName(),
1804  stale_member->Name() );
1805 
1806  member->Clone( *stale_member );
1807 
1808  propagate_bus_neighbors( subgraph );
1809  }
1810  }
1811  }
1812 
1813  aSubgraph->m_dirty = false;
1814 }
1815 
1816 
1817 std::shared_ptr<SCH_CONNECTION> CONNECTION_GRAPH::getDefaultConnection( SCH_ITEM* aItem,
1818  CONNECTION_SUBGRAPH* aSubgraph )
1819 {
1820  auto c = std::shared_ptr<SCH_CONNECTION>( nullptr );
1821 
1822  switch( aItem->Type() )
1823  {
1824  case SCH_PIN_T:
1825  {
1826  auto pin = static_cast<SCH_PIN*>( aItem );
1827 
1828  if( pin->IsPowerConnection() )
1829  c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
1830 
1831  break;
1832  }
1833 
1834  case SCH_GLOBAL_LABEL_T:
1835  case SCH_HIER_LABEL_T:
1836  case SCH_LABEL_T:
1837  {
1838  c = std::make_shared<SCH_CONNECTION>( aItem, aSubgraph->m_sheet );
1839  break;
1840  }
1841 
1842  default:
1843  break;
1844  }
1845 
1846  if( c )
1847  {
1848  c->SetGraph( this );
1849  c->ConfigureFromLabel( aSubgraph->GetNameForDriver( aItem ) );
1850  }
1851 
1852  return c;
1853 }
1854 
1855 
1857  SCH_CONNECTION* aSearch )
1858 {
1859  wxASSERT( aBusConnection->IsBus() );
1860 
1861  SCH_CONNECTION* match = nullptr;
1862 
1863  if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
1864  {
1865  // Vector bus: compare against index, because we allow the name
1866  // to be different
1867 
1868  for( const auto& bus_member : aBusConnection->Members() )
1869  {
1870  if( bus_member->VectorIndex() == aSearch->VectorIndex() )
1871  {
1872  match = bus_member.get();
1873  break;
1874  }
1875  }
1876  }
1877  else
1878  {
1879  // Group bus
1880  for( const auto& c : aBusConnection->Members() )
1881  {
1882  // Vector inside group: compare names, because for bus groups
1883  // we expect the naming to be consistent across all usages
1884  // TODO(JE) explain this in the docs
1885  if( c->Type() == CONNECTION_TYPE::BUS )
1886  {
1887  for( const auto& bus_member : c->Members() )
1888  {
1889  if( bus_member->LocalName() == aSearch->LocalName() )
1890  {
1891  match = bus_member.get();
1892  break;
1893  }
1894  }
1895  }
1896  else if( c->LocalName() == aSearch->LocalName() )
1897  {
1898  match = c.get();
1899  break;
1900  }
1901  }
1902  }
1903 
1904  return match;
1905 }
1906 
1907 
1909  const wxString& aOldName )
1910 {
1911  if( m_net_name_to_subgraphs_map.count( aOldName ) )
1912  {
1913  auto& vec = m_net_name_to_subgraphs_map.at( aOldName );
1914  vec.erase( std::remove( vec.begin(), vec.end(), aSubgraph ), vec.end() );
1915  }
1916 
1917  wxLogTrace( "CONN", "recacheSubgraphName: %s => %s", aOldName,
1918  aSubgraph->m_driver_connection->Name() );
1919 
1920  m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
1921 }
1922 
1923 
1924 std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( const wxString& aName )
1925 {
1926  if( m_bus_alias_cache.count( aName ) )
1927  return m_bus_alias_cache.at( aName );
1928 
1929  return nullptr;
1930 }
1931 
1932 
1933 std::vector<const CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
1934 {
1935  std::vector<const CONNECTION_SUBGRAPH*> ret;
1936 
1937  for( auto&& subgraph : m_subgraphs )
1938  {
1939  // Graph is supposed to be up-to-date before calling this
1940  wxASSERT( !subgraph->m_dirty );
1941 
1942  if( !subgraph->m_driver )
1943  continue;
1944 
1945  auto sheet = subgraph->m_sheet;
1946  auto connection = subgraph->m_driver->Connection( sheet );
1947 
1948  if( !connection->IsBus() )
1949  continue;
1950 
1951  auto labels = subgraph->GetBusLabels();
1952 
1953  if( labels.size() > 1 )
1954  {
1955  bool different = false;
1956  wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText();
1957 
1958  for( unsigned i = 1; i < labels.size(); ++i )
1959  {
1960  if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText() != first )
1961  {
1962  different = true;
1963  break;
1964  }
1965  }
1966 
1967  if( !different )
1968  continue;
1969 
1970  wxLogTrace( "CONN", "SG %ld (%s) has multiple bus labels", subgraph->m_code,
1971  connection->Name() );
1972 
1973  ret.push_back( subgraph );
1974  }
1975  }
1976 
1977  return ret;
1978 }
1979 
1980 
1982  const wxString& aNetName, const SCH_SHEET_PATH& aPath )
1983 {
1984  if( !m_net_name_to_subgraphs_map.count( aNetName ) )
1985  return nullptr;
1986 
1987  for( auto sg : m_net_name_to_subgraphs_map.at( aNetName ) )
1988  {
1989  // Cache is supposed to be valid by now
1990  wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
1991 
1992  if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
1993  return sg;
1994  }
1995 
1996  return nullptr;
1997 }
1998 
1999 
2001 {
2002  if( !m_net_name_to_subgraphs_map.count( aNetName ) )
2003  return nullptr;
2004 
2005  wxASSERT( !m_net_name_to_subgraphs_map.at( aNetName ).empty() );
2006 
2007  return m_net_name_to_subgraphs_map.at( aNetName )[0];
2008 }
2009 
2010 
2012 {
2013  if( !m_item_to_subgraph_map.count( aItem ) )
2014  return nullptr;
2015 
2016  CONNECTION_SUBGRAPH* ret = m_item_to_subgraph_map.at( aItem );
2017 
2018  while( ret->m_absorbed )
2019  ret = ret->m_absorbed_by;
2020 
2021  return ret;
2022 }
2023 
2024 
2026 {
2027  int error_count = 0;
2028 
2029  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2030 
2031  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2032 
2033  for( auto&& subgraph : m_subgraphs )
2034  {
2035  // Graph is supposed to be up-to-date before calling RunERC()
2036  wxASSERT( !subgraph->m_dirty );
2037 
2049  if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) && !subgraph->ResolveDrivers() )
2050  error_count++;
2051 
2052  if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT )
2053  && !ercCheckBusToNetConflicts( subgraph ) )
2054  error_count++;
2055 
2056  if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT )
2057  && !ercCheckBusToBusEntryConflicts( subgraph ) )
2058  error_count++;
2059 
2060  if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT )
2061  && !ercCheckBusToBusConflicts( subgraph ) )
2062  error_count++;
2063 
2064  if( settings.IsTestEnabled( ERCE_WIRE_DANGLING )
2065  && !ercCheckFloatingWires( subgraph ) )
2066  error_count++;
2067 
2068  // The following checks are always performed since they don't currently
2069  // have an option exposed to the user
2070 
2071  if( !ercCheckNoConnects( subgraph ) )
2072  error_count++;
2073 
2074  if( ( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED )
2075  || settings.IsTestEnabled( ERCE_GLOBLABEL ) ) && !ercCheckLabels( subgraph ) )
2076  error_count++;
2077  }
2078 
2079  // Hierarchical sheet checking is done at the schematic level
2080  if( settings.IsTestEnabled( ERCE_HIERACHICAL_LABEL ) )
2081  error_count += ercCheckHierSheets();
2082 
2083  return error_count;
2084 }
2085 
2086 
2088 {
2089  auto sheet = aSubgraph->m_sheet;
2090  auto screen = sheet.LastScreen();
2091 
2092  SCH_ITEM* net_item = nullptr;
2093  SCH_ITEM* bus_item = nullptr;
2094  SCH_CONNECTION conn( this );
2095 
2096  for( auto item : aSubgraph->m_items )
2097  {
2098  switch( item->Type() )
2099  {
2100  case SCH_LINE_T:
2101  {
2102  if( item->GetLayer() == LAYER_BUS )
2103  bus_item = ( !bus_item ) ? item : bus_item;
2104  else
2105  net_item = ( !net_item ) ? item : net_item;
2106  break;
2107  }
2108 
2109  case SCH_GLOBAL_LABEL_T:
2110  case SCH_SHEET_PIN_T:
2111  case SCH_HIER_LABEL_T:
2112  {
2113  SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2115 
2116  if( conn.IsBus() )
2117  bus_item = ( !bus_item ) ? item : bus_item;
2118  else
2119  net_item = ( !net_item ) ? item : net_item;
2120  break;
2121  }
2122 
2123  default:
2124  break;
2125  }
2126  }
2127 
2128  if( net_item && bus_item )
2129  {
2130  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_NET_CONFLICT );
2131  ercItem->SetItems( net_item, bus_item );
2132 
2133  SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
2134  screen->Append( marker );
2135 
2136  return false;
2137  }
2138 
2139  return true;
2140 }
2141 
2142 
2144 {
2145  wxString msg;
2146  auto sheet = aSubgraph->m_sheet;
2147  auto screen = sheet.LastScreen();
2148 
2149  SCH_ITEM* label = nullptr;
2150  SCH_ITEM* port = nullptr;
2151 
2152  for( auto item : aSubgraph->m_items )
2153  {
2154  switch( item->Type() )
2155  {
2156  case SCH_TEXT_T:
2157  case SCH_GLOBAL_LABEL_T:
2158  {
2159  if( !label && item->Connection( sheet )->IsBus() )
2160  label = item;
2161  break;
2162  }
2163 
2164  case SCH_SHEET_PIN_T:
2165  case SCH_HIER_LABEL_T:
2166  {
2167  if( !port && item->Connection( sheet )->IsBus() )
2168  port = item;
2169  break;
2170  }
2171 
2172  default:
2173  break;
2174  }
2175  }
2176 
2177  if( label && port )
2178  {
2179  bool match = false;
2180 
2181  for( const auto& member : label->Connection( sheet )->Members() )
2182  {
2183  for( const auto& test : port->Connection( sheet )->Members() )
2184  {
2185  if( test != member && member->Name() == test->Name() )
2186  {
2187  match = true;
2188  break;
2189  }
2190  }
2191 
2192  if( match )
2193  break;
2194  }
2195 
2196  if( !match )
2197  {
2198  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_TO_BUS_CONFLICT );
2199  ercItem->SetItems( label, port );
2200 
2201  SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
2202  screen->Append( marker );
2203 
2204  return false;
2205  }
2206  }
2207 
2208  return true;
2209 }
2210 
2211 
2213 {
2214  bool conflict = false;
2215  auto sheet = aSubgraph->m_sheet;
2216  auto screen = sheet.LastScreen();
2217 
2218  SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
2219  SCH_ITEM* bus_wire = nullptr;
2220 
2221  for( auto item : aSubgraph->m_items )
2222  {
2223  switch( item->Type() )
2224  {
2225  case SCH_BUS_WIRE_ENTRY_T:
2226  {
2227  if( !bus_entry )
2228  bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
2229  break;
2230  }
2231 
2232  default:
2233  break;
2234  }
2235  }
2236 
2237  if( bus_entry && bus_entry->m_connected_bus_item )
2238  {
2239  bus_wire = bus_entry->m_connected_bus_item;
2240 
2241  wxASSERT( bus_wire->Type() == SCH_LINE_T );
2242 
2243  // In some cases, the connection list (SCH_CONNECTION*) can be null.
2244  // Skip null connections.
2245  if( bus_entry->Connection( sheet ) && bus_wire->Type() == SCH_LINE_T
2246  && bus_wire->Connection( sheet ) )
2247  {
2248  conflict = true;
2249 
2250  auto test_name = bus_entry->Connection( sheet )->Name( true );
2251 
2252  for( const auto& member : bus_wire->Connection( sheet )->Members() )
2253  {
2254  if( member->Type() == CONNECTION_TYPE::BUS )
2255  {
2256  for( const auto& sub_member : member->Members() )
2257  {
2258  if( sub_member->Name( true ) == test_name )
2259  conflict = false;
2260  }
2261  }
2262  else if( member->Name( true ) == test_name )
2263  {
2264  conflict = false;
2265  }
2266  }
2267  }
2268  }
2269 
2270  // Don't report warnings if this bus member has been overridden by a higher priority power pin
2271  // or global label
2272  if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
2274  conflict = false;
2275 
2276  if( conflict )
2277  {
2278  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_CONFLICT );
2279  ercItem->SetItems( bus_entry, bus_wire );
2280 
2281  SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
2282  screen->Append( marker );
2283 
2284  return false;
2285  }
2286 
2287  return true;
2288 }
2289 
2290 
2291 // TODO(JE) Check sheet pins here too?
2293 {
2294  wxString msg;
2295  const SCH_SHEET_PATH& sheet = aSubgraph->m_sheet;
2296  SCH_SCREEN* screen = sheet.LastScreen();
2297  bool ok = true;
2298 
2299  if( aSubgraph->m_no_connect != nullptr )
2300  {
2301  bool has_invalid_items = false;
2302  bool has_other_items = false;
2303  SCH_PIN* pin = nullptr;
2304  std::vector<SCH_ITEM*> invalid_items;
2305 
2306  // Any subgraph that contains both a pin and a no-connect should not
2307  // contain any other driving items.
2308 
2309  for( auto item : aSubgraph->m_items )
2310  {
2311  switch( item->Type() )
2312  {
2313  case SCH_PIN_T:
2314  pin = static_cast<SCH_PIN*>( item );
2315  has_other_items = true;
2316  break;
2317 
2318  case SCH_LINE_T:
2319  case SCH_JUNCTION_T:
2320  case SCH_NO_CONNECT_T:
2321  break;
2322 
2323  default:
2324  has_invalid_items = true;
2325  has_other_items = true;
2326  invalid_items.push_back( item );
2327  }
2328  }
2329 
2330  if( pin && has_invalid_items )
2331  {
2332  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_CONNECTED );
2333  ercItem->SetItems( pin );
2334 
2335  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2336  screen->Append( marker );
2337 
2338  ok = false;
2339  }
2340 
2341  if( !has_other_items )
2342  {
2343  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_NOCONNECT_NOT_CONNECTED );
2344  ercItem->SetItems( aSubgraph->m_no_connect );
2345 
2346  SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
2347  screen->Append( marker );
2348 
2349  ok = false;
2350  }
2351  }
2352  else
2353  {
2354  bool has_other_connections = false;
2355  std::vector<SCH_PIN*> pins;
2356 
2357  // Any subgraph that lacks a no-connect and contains a pin should also
2358  // contain at least one other potential driver
2359 
2360  for( SCH_ITEM* item : aSubgraph->m_items )
2361  {
2362  switch( item->Type() )
2363  {
2364  case SCH_PIN_T:
2365  {
2366  if( !pins.empty() )
2367  has_other_connections = true;
2368 
2369  pins.emplace_back( static_cast<SCH_PIN*>( item ) );
2370 
2371  break;
2372  }
2373 
2374  default:
2375  if( aSubgraph->GetDriverPriority( item ) != CONNECTION_SUBGRAPH::PRIORITY::NONE )
2376  has_other_connections = true;
2377 
2378  break;
2379  }
2380  }
2381 
2382  // For many checks, we can just use the first pin
2383  SCH_PIN* pin = pins.empty() ? nullptr : pins[0];
2384 
2385  // Check if invisible power input pins connect to anything else via net name,
2386  // but not for power symbols as the ones in the standard library all have invisible pins
2387  // and we want to throw unconnected errors for those even if they are connected to other
2388  // net items by name, because usually failing to connect them graphically is a mistake
2389  if( pin && !has_other_connections
2391  && !pin->IsVisible()
2392  && !pin->GetLibPin()->GetParent()->IsPower() )
2393  {
2394  wxString name = pin->Connection( sheet )->Name();
2395  wxString local_name = pin->Connection( sheet )->Name( true );
2396 
2397  if( m_global_label_cache.count( name ) ||
2398  ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
2399  {
2400  has_other_connections = true;
2401  }
2402  }
2403 
2404  // Only one pin, and it's not a no-connect pin
2405  if( pin && !has_other_connections && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC )
2406  {
2407  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2408  ercItem->SetItems( pin );
2409 
2410  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2411  screen->Append( marker );
2412 
2413  ok = false;
2414  }
2415 
2416  // If there are multiple pins in this SG, they might be indirectly connected (by netname)
2417  // rather than directly connected (by wires). We want to flag dangling pins even if they
2418  // join nets with another pin, as it's often a mistake
2419  if( pins.size() > 1 )
2420  {
2421  for( SCH_PIN* testPin : pins )
2422  {
2423  // We only apply this test to power symbols, because other symbols have invisible
2424  // pins that are meant to be dangling, but the KiCad standard library power symbols
2425  // have invisible pins that are *not* meant to be dangling.
2426  if( testPin->GetLibPin()->GetParent()->IsPower() &&
2427  testPin->ConnectedItems( sheet ).empty() )
2428  {
2429  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_PIN_NOT_CONNECTED );
2430  ercItem->SetItems( testPin );
2431 
2432  SCH_MARKER* marker = new SCH_MARKER( ercItem,
2433  testPin->GetTransformedPosition() );
2434  screen->Append( marker );
2435 
2436  ok = false;
2437  }
2438  }
2439  }
2440  }
2441 
2442  return ok;
2443 }
2444 
2445 
2447 {
2448  if( aSubgraph->m_driver )
2449  return true;
2450 
2451  std::vector<SCH_LINE*> wires;
2452 
2453  // We've gotten this far, so we know we have no valid driver. All we need to do is check
2454  // for a wire that we can place the error on.
2455 
2456  for( SCH_ITEM* item : aSubgraph->m_items )
2457  {
2458  if( item->Type() == SCH_LINE_T && item->GetLayer() == LAYER_WIRE )
2459  wires.emplace_back( static_cast<SCH_LINE*>( item ) );
2460  }
2461 
2462  if( !wires.empty() )
2463  {
2464  SCH_SCREEN* screen = aSubgraph->m_sheet.LastScreen();
2465 
2466  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_WIRE_DANGLING );
2467  ercItem->SetItems( wires[0],
2468  wires.size() > 1 ? wires[1] : nullptr,
2469  wires.size() > 2 ? wires[2] : nullptr,
2470  wires.size() > 3 ? wires[3] : nullptr );
2471 
2472  SCH_MARKER* marker = new SCH_MARKER( ercItem, wires[0]->GetPosition() );
2473  screen->Append( marker );
2474 
2475  return false;
2476  }
2477 
2478  return true;
2479 }
2480 
2481 
2483 {
2484  // Label connection rules:
2485  // Local labels are flagged if they don't connect to any pins and don't have a no-connect
2486  // Global labels are flagged if they appear only once, don't connect to any local labels,
2487  // and don't have a no-connect marker
2488 
2489  // So, if there is a no-connect, we will never generate a warning here
2490  if( aSubgraph->m_no_connect )
2491  return true;
2492 
2493  SCH_TEXT* text = nullptr;
2494  bool has_other_connections = false;
2495 
2496  for( auto item : aSubgraph->m_items )
2497  {
2498  switch( item->Type() )
2499  {
2500  case SCH_LABEL_T:
2501  case SCH_GLOBAL_LABEL_T:
2502  case SCH_HIER_LABEL_T:
2503  text = static_cast<SCH_TEXT*>( item );
2504  break;
2505 
2506  case SCH_PIN_T:
2507  case SCH_SHEET_PIN_T:
2508  has_other_connections = true;
2509  break;
2510 
2511  default:
2512  break;
2513  }
2514  }
2515 
2516  if( !text )
2517  return true;
2518 
2519  bool isGlobal = text->Type() == SCH_GLOBAL_LABEL_T;
2520 
2521  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2522 
2523  // Global label check can be disabled independently
2524  if( !m_schematic->ErcSettings().IsTestEnabled( ERCE_GLOBLABEL ) && isGlobal )
2525  return true;
2526 
2527  wxString name = EscapeString( text->GetShownText(), CTX_NETNAME );
2528 
2529  if( isGlobal )
2530  {
2531  // This will be set to true if the global is connected to a pin above, but we
2532  // want to reset this to false so that globals get flagged if they only have a
2533  // single instance
2534  has_other_connections = false;
2535 
2536  if( m_net_name_to_subgraphs_map.count( name ) )
2537  {
2538  if( m_net_name_to_subgraphs_map.at( name ).size() > 1 )
2539  has_other_connections = true;
2540  else if( aSubgraph->m_multiple_drivers )
2541  has_other_connections = true;
2542  }
2543  }
2544  else if( text->Type() == SCH_HIER_LABEL_T )
2545  {
2546  // For a hier label, check if the parent pin is connected
2547  if( aSubgraph->m_hier_parent &&
2548  ( aSubgraph->m_hier_parent->m_strong_driver ||
2549  aSubgraph->m_hier_parent->m_drivers.size() > 1))
2550  {
2551  // For now, a simple check: if there is more than one driver, the parent is probably
2552  // connected elsewhere (because at least one driver will be the hier pin itself)
2553  has_other_connections = true;
2554  }
2555  }
2556  else
2557  {
2558  auto pair = std::make_pair( aSubgraph->m_sheet, name );
2559 
2560  if( m_local_label_cache.count( pair ) && m_local_label_cache.at( pair ).size() > 1 )
2561  has_other_connections = true;
2562  }
2563 
2564  if( !has_other_connections )
2565  {
2566  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( isGlobal ? ERCE_GLOBLABEL
2568  ercItem->SetItems( text );
2569 
2570  SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
2571  aSubgraph->m_sheet.LastScreen()->Append( marker );
2572 
2573  return false;
2574  }
2575 
2576  return true;
2577 }
2578 
2579 
2581 {
2582  int errors = 0;
2583 
2584  for( const SCH_SHEET_PATH& sheet : m_sheetList )
2585  {
2586  for( SCH_ITEM* item : sheet.LastScreen()->Items() )
2587  {
2588  if( item->Type() != SCH_SHEET_T )
2589  continue;
2590 
2591  SCH_SHEET* parentSheet = static_cast<SCH_SHEET*>( item );
2592 
2593  std::map<wxString, SCH_SHEET_PIN*> pins;
2594 
2595  for( SCH_SHEET_PIN* pin : parentSheet->GetPins() )
2596  pins[pin->GetText()] = pin;
2597 
2598  for( SCH_ITEM* subItem : parentSheet->GetScreen()->Items() )
2599  {
2600  if( subItem->Type() == SCH_HIER_LABEL_T )
2601  {
2602  SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( subItem );
2603  pins.erase( label->GetText() );
2604  }
2605  }
2606 
2607  for( const std::pair<const wxString, SCH_SHEET_PIN*>& unmatched : pins )
2608  {
2609  std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_HIERACHICAL_LABEL );
2610  ercItem->SetItems( unmatched.second );
2611  ercItem->SetErrorMessage( wxString::Format(
2612  _( "Sheet port %s has no matching hierarchical label inside the sheet" ),
2613  unmatched.first ) );
2614 
2615  SCH_MARKER* marker = new SCH_MARKER( ercItem, unmatched.second->GetPosition() );
2616  sheet.LastScreen()->Append( marker );
2617 
2618  errors++;
2619  }
2620  }
2621  }
2622 
2623  return errors;
2624 }
CONNECTION_SUBGRAPH * FindSubgraphByName(const wxString &aNetName, const SCH_SHEET_PATH &aPath)
Returns the subgraph for a given net name on a given sheet.
void Stop()
save the time when this function was called, and set the counter stane to stop
Definition: profile.h:82
power input (GND, VCC for ICs). Must be connected to a power output.
SCH_SHEET_PATH m_sheet
SCH_SHEET_LIST.
void SetBusCode(int aCode)
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
LIB_PIN * GetLibPin() const
Definition: sch_pin.h:67
void propagateToNeighbors(CONNECTION_SUBGRAPH *aSubgraph)
Updates all neighbors of a subgraph with this one's connectivity info.
bool ercCheckBusToBusEntryConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting bus entry to bus connections.
SCH_SHEET * GetParent() const
Get the parent sheet object of this sheet pin.
Definition: sch_sheet.h:168
void buildConnectionGraph()
Generates the connection graph (after all item connectivity has been updated)
SCH_SHEET_LIST GetSheets() const
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:89
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
wxString GetDefaultNetName(const SCH_SHEET_PATH &aPath)
Definition: sch_pin.cpp:220
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 ...
std::shared_ptr< BUS_ALIAS > GetBusAlias(const wxString &aName)
Returns a bus alias pointer for the given name if it exists (from cache)
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...
double msecs(bool aSinceLast=false)
Definition: profile.h:143
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false)
Updates the connection graph for the given list of sheets.
bool IsVisible() const
Definition: sch_pin.h:108
LIB_PART * GetParent() const
Definition: lib_item.h:182
void Absorb(CONNECTION_SUBGRAPH *aOther)
Combines another subgraph on the same sheet into this one.
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:149
This item represents a bus group.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
virtual bool ConnectionPropagatesTo(const EDA_ITEM *aItem) const
Returns true if this item should propagate connection info to aItem.
Definition: sch_item.h:431
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:115
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
bool ercCheckFloatingWires(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for floating wires.
std::vector< SCH_SHEET_PIN * > m_hier_pins
bool m_local_driver
True if the driver is a local (i.e. non-global) type.
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
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.
void ConfigureFromLabel(const wxString &aLabel)
Configures the connection given a label.
void SetDriver(SCH_ITEM *aItem)
void SetName(const wxString &aName)
NET_MAP m_net_code_to_subgraphs_map
std::vector< SCH_ITEM * > m_items
SCHEMATIC * m_schematic
The schematic this graph represents.
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
wxString LocalName() const
#define kv
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:282
The class PROF_COUNTER is a small class to help profiling.
Definition: profile.h:44
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
A subgraph is a set of items that are electrically connected on a single sheet.
bool ResolveDrivers(bool aCreateMarkers=false)
Determines which potential driver should drive the subgraph.
void updateItemConnectivity(const SCH_SHEET_PATH &aSheet, const std::vector< SCH_ITEM * > &aItemList)
Updates the graphical connectivity between items (i.e.
int ercCheckHierSheets()
Checks that a hierarchical sheet has at least one matching label inside the sheet for each port on th...
void pop_back()
Forwarded method from std::vector.
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
void recacheSubgraphName(CONNECTION_SUBGRAPH *aSubgraph, const wxString &aOldName)
bool ercCheckBusToNetConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting connections between net and bus labels.
CONNECTION_GRAPH * m_graph
SCH_CONNECTION * InitializeConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Creates a new connection object associated with this object.
Definition: sch_item.cpp:190
wxString Name(bool aIgnoreSheet=false) const
const wxString & GetNameForDriver(SCH_ITEM *aItem)
Returns the candidate net name for a driver.
int assignNewNetCode(SCH_CONNECTION &aConnection)
Helper to assign a new net code to a connection.
size_t size() const
Forwarded method from std::vector.
void SetSubgraphCode(int aCode)
bool ercCheckNoConnects(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for proper presence or absence of no-connect symbols.
std::vector< SCH_ITEM * > m_items
void SetLayer(SCH_LAYER_ID aLayer)
Set the layer this item is on.
Definition: sch_item.h:286
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
void AddItem(SCH_ITEM *aItem)
Adds a new item to the subgraph.
int SubgraphCode() const
Functions to provide common constants and other functions to assist in making a consistent UI.
bool m_strong_driver
True if the driver is "strong": a label or power object.
std::vector< SCH_ITEM * > m_drivers
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
Container for ERC settings.
Definition: erc_settings.h:88
SCH_SHEET_PATH.
long VectorIndex() const
void Clone(SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_text.cpp:483
wxPoint GetPosition() const override
Definition: sch_text.h:313
CONNECTION_SUBGRAPH * FindFirstSubgraphByName(const wxString &aNetName)
Retrieves a subgraph for the given net name, if one exists.
bool IsDriver() const
Checks if the SCH_ITEM this connection is attached to can drive connections Drivers can be labels,...
std::vector< SCH_HIERLABEL * > m_hier_ports
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
void assignNetCodesToBus(SCH_CONNECTION *aConnection)
Ensures all members of the bus connection have a valid net code assigned.
SCH_CONNECTION * Connection(const SCH_SHEET_PATH &aPath) const
Retrieve the connection associated with this object in the given sheet.
Definition: sch_item.cpp:150
This item represents a net.
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieves a list of the SCH_PINs for the given sheet path.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:216
static const wxChar ConnProfileMask[]
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition: sch_item.h:279
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition: sch_sheet.h:362
static bool m_allowRealTime
bool IsNet() const
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
bool IsPower() const
SCH_SCREEN * LastScreen()
Function LastScreen.
const char * name
Definition: DXF_plotter.cpp:60
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
SCH_ITEM_SET & ConnectedItems(const SCH_SHEET_PATH &aPath)
Retrieves the set of items connected to this item on the given sheet.
Definition: sch_item.cpp:178
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:131
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
#define _(s)
Definition: 3d_actions.cpp:33
SCH_SHEET_LIST m_sheetList
std::shared_ptr< SCH_CONNECTION > getDefaultConnection(SCH_ITEM *aItem, CONNECTION_SUBGRAPH *aSubgraph)
Builds a new default connection for the given item based on its properties.
std::vector< SCH_ITEM * > GetBusLabels() const
Returns all the bus labels attached to this subgraph (if any)
virtual wxString GetSelectMenuText(EDA_UNITS aUnits) const
Function GetSelectMenuText returns the text to display to be used in the selection clarification cont...
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
These Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which ar...
Definition: string.cpp:77
ELECTRICAL_PINTYPE GetType() const
Definition: sch_pin.cpp:74
EE_RTREE & Items()
Definition: sch_screen.h:158
Schematic symbol object.
Definition: sch_component.h:80
std::map< wxString, int > m_bus_name_to_code_map
The common library.
std::map< wxString, int > m_net_name_to_code_map
CONNECTION_SUBGRAPH * m_hier_parent
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
Class for a wire to bus entry.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers of advanced config.
void Show(std::ostream &aStream=std::cerr)
Print the elapsed time (in a suitable unit) to a stream.
Definition: profile.h:99
CONNECTION_SUBGRAPH * GetSubgraphForItem(SCH_ITEM *aItem)
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:131
void SetType(CONNECTION_TYPE aType)
SCH_ITEM * m_no_connect
No-connect item in graph, if any.
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
not connected (must be left open)
bool IsBus() const
bool IsSubsetOf(SCH_CONNECTION *aOther) const
Returns true if this connection is contained within aOther (but not the same as aOther)
std::vector< const CONNECTION_SUBGRAPH * > GetBusesNeedingMigration()
Determines which subgraphs have more than one conflicting bus label.
CONNECTION_TYPE Type() const
bool ercCheckLabels(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for proper connection of labels.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:194
std::map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:127
int RunERC()
Runs electrical rule checks on the connectivity graph.
void SetNetCode(int aCode)
wxString GetNetName() const
Returns the fully-qualified net name for this subgraph (if one exists)
wxString GetName() const
Definition: sch_pin.cpp:65
This item represents a bus vector.
static SCH_CONNECTION * matchBusMember(SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
Search for a matching bus member inside a bus connection.
bool ercCheckBusToBusConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting connections between two bus items.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
std::unordered_map< SCH_ITEM *, wxString > m_driver_name_cache
A cache of escaped netnames from schematic items.
wxPoint GetPosition() const override
wxPoint GetTransformedPosition() const
Returns the pin's position in global coordinates.
Definition: sch_pin.cpp:252
SCH_LINE * GetBus(const wxPoint &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T)
Definition: sch_screen.h:439
void UpdateItemConnections()
Updates all items to match the driver connection.