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