KiCad PCB EDA Suite
CONNECTION_GRAPH Class Reference

Calculates the connectivity of a schematic and generates netlists. More...

#include <connection_graph.h>

Public Member Functions

 CONNECTION_GRAPH (SCHEMATIC *aSchematic=nullptr)
 
 ~CONNECTION_GRAPH ()
 
void Reset ()
 
void SetSchematic (SCHEMATIC *aSchematic)
 
void Recalculate (const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false)
 Updates the connection graph for the given list of sheets. More...
 
std::shared_ptr< BUS_ALIASGetBusAlias (const wxString &aName)
 Returns a bus alias pointer for the given name if it exists (from cache) More...
 
std::vector< const CONNECTION_SUBGRAPH * > GetBusesNeedingMigration ()
 Determines which subgraphs have more than one conflicting bus label. More...
 
int RunERC ()
 Runs electrical rule checks on the connectivity graph. More...
 
const NET_MAPGetNetMap () const
 
CONNECTION_SUBGRAPHFindSubgraphByName (const wxString &aNetName, const SCH_SHEET_PATH &aPath)
 Returns the subgraph for a given net name on a given sheet. More...
 
CONNECTION_SUBGRAPHFindFirstSubgraphByName (const wxString &aNetName)
 Retrieves a subgraph for the given net name, if one exists. More...
 
CONNECTION_SUBGRAPHGetSubgraphForItem (SCH_ITEM *aItem)
 

Static Public Attributes

static bool m_allowRealTime = true
 

Private Member Functions

void updateItemConnectivity (const SCH_SHEET_PATH &aSheet, const std::vector< SCH_ITEM * > &aItemList)
 Updates the graphical connectivity between items (i.e. More...
 
void buildConnectionGraph ()
 Generates the connection graph (after all item connectivity has been updated) More...
 
int assignNewNetCode (SCH_CONNECTION &aConnection)
 Helper to assign a new net code to a connection. More...
 
void assignNetCodesToBus (SCH_CONNECTION *aConnection)
 Ensures all members of the bus connection have a valid net code assigned. More...
 
void propagateToNeighbors (CONNECTION_SUBGRAPH *aSubgraph)
 Updates all neighbors of a subgraph with this one's connectivity info. More...
 
std::shared_ptr< SCH_CONNECTIONgetDefaultConnection (SCH_ITEM *aItem, const SCH_SHEET_PATH &aSheet)
 Builds a new default connection for the given item based on its properties. More...
 
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. More...
 
bool ercCheckBusToBusConflicts (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for conflicting connections between two bus items. More...
 
bool ercCheckBusToBusEntryConflicts (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for conflicting bus entry to bus connections. More...
 
bool ercCheckNoConnects (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for proper presence or absence of no-connect symbols. More...
 
bool ercCheckLabels (const CONNECTION_SUBGRAPH *aSubgraph)
 Checks one subgraph for proper connection of labels. More...
 

Static Private Member Functions

static SCH_CONNECTIONmatchBusMember (SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
 Search for a matching bus member inside a bus connection. More...
 

Private Attributes

std::unordered_set< SCH_ITEM * > m_items
 
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
 
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
 
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
 
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
 
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
 
std::map< wxString, int > m_net_name_to_code_map
 
std::map< wxString, int > m_bus_name_to_code_map
 
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
 
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
 
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
 
std::map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map
 
NET_MAP m_net_code_to_subgraphs_map
 
int m_last_net_code
 
int m_last_bus_code
 
int m_last_subgraph_code
 
SCHEMATICm_schematic
 The schematic this graph represents. More...
 

Detailed Description

Calculates the connectivity of a schematic and generates netlists.

Definition at line 220 of file connection_graph.h.

Constructor & Destructor Documentation

◆ CONNECTION_GRAPH()

CONNECTION_GRAPH::CONNECTION_GRAPH ( SCHEMATIC aSchematic = nullptr)
inline

Definition at line 223 of file connection_graph.h.

223  :
224  m_last_net_code( 1 ),
225  m_last_bus_code( 1 ),
227  m_schematic( aSchematic )
228  {}
SCHEMATIC * m_schematic
The schematic this graph represents.

◆ ~CONNECTION_GRAPH()

CONNECTION_GRAPH::~CONNECTION_GRAPH ( )
inline

Definition at line 230 of file connection_graph.h.

231  {
232  Reset();
233  }

References Reset().

Member Function Documentation

◆ assignNetCodesToBus()

void CONNECTION_GRAPH::assignNetCodesToBus ( SCH_CONNECTION aConnection)
private

Ensures all members of the bus connection have a valid net code assigned.

Parameters
aConnectionis a bus connection

Definition at line 1517 of file connection_graph.cpp.

1518 {
1519  auto connections_to_check( aConnection->Members() );
1520 
1521  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1522  {
1523  auto member = connections_to_check[i];
1524 
1525  if( member->IsBus() )
1526  {
1527  connections_to_check.insert( connections_to_check.end(),
1528  member->Members().begin(),
1529  member->Members().end() );
1530  continue;
1531  }
1532 
1533  assignNewNetCode( *member );
1534  }
1535 }
int assignNewNetCode(SCH_CONNECTION &aConnection)
Helper to assign a new net code to a connection.
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()

References assignNewNetCode(), and SCH_CONNECTION::Members().

Referenced by buildConnectionGraph().

◆ assignNewNetCode()

int CONNECTION_GRAPH::assignNewNetCode ( SCH_CONNECTION aConnection)
private

Helper to assign a new net code to a connection.

Returns
the assigned code

Definition at line 1497 of file connection_graph.cpp.

1498 {
1499  int code;
1500 
1501  if( m_net_name_to_code_map.count( aConnection.Name() ) )
1502  {
1503  code = m_net_name_to_code_map.at( aConnection.Name() );
1504  }
1505  else
1506  {
1507  code = m_last_net_code++;
1508  m_net_name_to_code_map[ aConnection.Name() ] = code;
1509  }
1510 
1511  aConnection.SetNetCode( code );
1512 
1513  return code;
1514 }
const wxString & Name(bool aIgnoreSheet=false) const
std::map< wxString, int > m_net_name_to_code_map
void SetNetCode(int aCode)

References m_last_net_code, m_net_name_to_code_map, SCH_CONNECTION::Name(), and SCH_CONNECTION::SetNetCode().

Referenced by assignNetCodesToBus(), and buildConnectionGraph().

◆ buildConnectionGraph()

void CONNECTION_GRAPH::buildConnectionGraph ( )
private

Generates the connection graph (after all item connectivity has been updated)

In the first phase, the algorithm iterates over all items, and then over all items that are connected (graphically) to each item, placing them into CONNECTION_SUBGRAPHs. Items that can potentially drive connectivity (i.e. labels, pins, etc.) are added to the m_drivers vector of the subgraph.

In the second phase, each subgraph is resolved. To resolve a subgraph, the driver is first selected by CONNECTION_SUBGRAPH::ResolveDrivers(), and then the connection for the chosen driver is propagated to all the other items in the subgraph.

TODO(JE)

It would be good if net codes were preserved as much as possible when generating netlists, so that unnamed nets don't keep shifting around when you regenerate.

Right now, we are clearing out the old connections up in UpdateItemConnectivity(), but that is useful information, so maybe we need to just set the dirty flag or something.

That way, ResolveDrivers() can check what the driver of the subgraph was previously, and if it is in the situation of choosing between equal candidates for an auto-generated net name, pick the previous one.

N.B. the old algorithm solves this by sorting the possible net names alphabetically, so as long as the same refdes components are involved, the net will be the same.

Definition at line 644 of file connection_graph.cpp.

645 {
646  // Recache all bus aliases for later use
647  wxCHECK_RET( m_schematic, "Connection graph cannot be built without schematic pointer" );
648 
649  SCH_SHEET_LIST all_sheets = m_schematic->GetSheets();
650 
651  for( unsigned i = 0; i < all_sheets.size(); i++ )
652  {
653  for( const auto& alias : all_sheets[i].LastScreen()->GetBusAliases() )
654  m_bus_alias_cache[ alias->GetName() ] = alias;
655  }
656 
657  // Build subgraphs from items (on a per-sheet basis)
658 
659  for( SCH_ITEM* item : m_items )
660  {
661  for( const auto& it : item->m_connection_map )
662  {
663  const auto sheet = it.first;
664  auto connection = it.second;
665 
666  if( connection->SubgraphCode() == 0 )
667  {
668  auto subgraph = new CONNECTION_SUBGRAPH( this );
669 
670  subgraph->m_code = m_last_subgraph_code++;
671  subgraph->m_sheet = sheet;
672 
673  subgraph->AddItem( item );
674 
675  connection->SetSubgraphCode( subgraph->m_code );
676  m_item_to_subgraph_map[item] = subgraph;
677 
678  std::list<SCH_ITEM*> members;
679 
680  auto get_items =
681  [&]( SCH_ITEM* aItem ) -> bool
682  {
683  auto* conn = aItem->Connection( sheet );
684 
685  if( !conn )
686  conn = aItem->InitializeConnection( sheet, this );
687 
688  return ( conn->SubgraphCode() == 0 );
689  };
690 
691  std::copy_if( item->ConnectedItems( sheet ).begin(),
692  item->ConnectedItems( sheet ).end(),
693  std::back_inserter( members ), get_items );
694 
695  for( auto connected_item : members )
696  {
697  if( connected_item->Type() == SCH_NO_CONNECT_T )
698  subgraph->m_no_connect = connected_item;
699 
700  auto connected_conn = connected_item->Connection( sheet );
701 
702  wxASSERT( connected_conn );
703 
704  if( connected_conn->SubgraphCode() == 0 )
705  {
706  connected_conn->SetSubgraphCode( subgraph->m_code );
707  m_item_to_subgraph_map[connected_item] = subgraph;
708  subgraph->AddItem( connected_item );
709 
710  std::copy_if( connected_item->ConnectedItems( sheet ).begin(),
711  connected_item->ConnectedItems( sheet ).end(),
712  std::back_inserter( members ), get_items );
713  }
714  }
715 
716  subgraph->m_dirty = true;
717  m_subgraphs.push_back( subgraph );
718  }
719  }
720  }
721 
742  // Resolve drivers for subgraphs and propagate connectivity info
743 
744  // We don't want to spin up a new thread for fewer than 8 nets (overhead costs)
745  size_t parallelThreadCount = std::min<size_t>( std::thread::hardware_concurrency(),
746  ( m_subgraphs.size() + 3 ) / 4 );
747 
748  std::atomic<size_t> nextSubgraph( 0 );
749  std::vector<std::future<size_t>> returns( parallelThreadCount );
750  std::vector<CONNECTION_SUBGRAPH*> dirty_graphs;
751 
752  std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( dirty_graphs ),
753  [&] ( const CONNECTION_SUBGRAPH* candidate )
754  {
755  return candidate->m_dirty;
756  } );
757 
758  auto update_lambda = [&nextSubgraph, &dirty_graphs]() -> size_t
759  {
760  for( size_t subgraphId = nextSubgraph++; subgraphId < dirty_graphs.size(); subgraphId = nextSubgraph++ )
761  {
762  auto subgraph = dirty_graphs[subgraphId];
763 
764  if( !subgraph->m_dirty )
765  continue;
766 
767  // Special processing for some items
768  for( auto item : subgraph->m_items )
769  {
770  switch( item->Type() )
771  {
772  case SCH_NO_CONNECT_T:
773  subgraph->m_no_connect = item;
774  break;
775 
777  subgraph->m_bus_entry = item;
778  break;
779 
780  case SCH_PIN_T:
781  {
782  auto pin = static_cast<SCH_PIN*>( item );
783 
784  if( pin->GetType() == ELECTRICAL_PINTYPE::PT_NC )
785  subgraph->m_no_connect = item;
786 
787  break;
788  }
789 
790  default:
791  break;
792  }
793  }
794 
795  if( !subgraph->ResolveDrivers() )
796  {
797  subgraph->m_dirty = false;
798  }
799  else
800  {
801  // Now the subgraph has only one driver
802  SCH_ITEM* driver = subgraph->m_driver;
803  SCH_SHEET_PATH sheet = subgraph->m_sheet;
804  SCH_CONNECTION* connection = driver->Connection( sheet );
805 
806  // TODO(JE) This should live in SCH_CONNECTION probably
807  switch( driver->Type() )
808  {
809  case SCH_LABEL_T:
810  case SCH_GLOBAL_LABEL_T:
811  case SCH_HIER_LABEL_T:
812  {
813  auto text = static_cast<SCH_TEXT*>( driver );
814  connection->ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
815  break;
816  }
817  case SCH_SHEET_PIN_T:
818  {
819  auto pin = static_cast<SCH_SHEET_PIN*>( driver );
820  connection->ConfigureFromLabel( EscapeString( pin->GetShownText(), CTX_NETNAME ) );
821  break;
822  }
823  case SCH_PIN_T:
824  {
825  auto pin = static_cast<SCH_PIN*>( driver );
826  // NOTE(JE) GetDefaultNetName is not thread-safe.
827  connection->ConfigureFromLabel( pin->GetDefaultNetName( sheet ) );
828 
829  break;
830  }
831  default:
832  wxLogTrace( "CONN", "Driver type unsupported: %s",
834  break;
835  }
836 
837  connection->SetDriver( driver );
838  connection->ClearDirty();
839 
840  subgraph->m_dirty = false;
841  }
842  }
843 
844  return 1;
845  };
846 
847  if( parallelThreadCount == 1 )
848  update_lambda();
849  else
850  {
851  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
852  returns[ii] = std::async( std::launch::async, update_lambda );
853 
854  // Finalize the threads
855  for( size_t ii = 0; ii < parallelThreadCount; ++ii )
856  returns[ii].wait();
857  }
858 
859  // Now discard any non-driven subgraphs from further consideration
860 
861  std::copy_if( m_subgraphs.begin(), m_subgraphs.end(), std::back_inserter( m_driver_subgraphs ),
862  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
863  {
864  return candidate->m_driver;
865  } );
866 
867  // Check for subgraphs with the same net name but only weak drivers.
868  // For example, two wires that are both connected to hierarchical
869  // sheet pins that happen to have the same name, but are not the same.
870 
871  for( auto&& subgraph : m_driver_subgraphs )
872  {
873  wxString full_name = subgraph->m_driver_connection->Name();
874  wxString name = subgraph->m_driver_connection->Name( true );
875  m_net_name_to_subgraphs_map[full_name].emplace_back( subgraph );
876 
877  subgraph->m_dirty = true;
878 
879  if( subgraph->m_strong_driver )
880  {
881  SCH_ITEM* driver = subgraph->m_driver;
882  SCH_SHEET_PATH sheet = subgraph->m_sheet;
883 
884  switch( driver->Type() )
885  {
886  case SCH_LABEL_T:
887  case SCH_HIER_LABEL_T:
888  {
889  m_local_label_cache[std::make_pair( sheet, name )].push_back( subgraph );
890  break;
891  }
892  case SCH_GLOBAL_LABEL_T:
893  {
894  m_global_label_cache[name].push_back( subgraph );
895  break;
896  }
897  case SCH_PIN_T:
898  {
899  auto pin = static_cast<SCH_PIN*>( driver );
900  wxASSERT( pin->IsPowerConnection() );
901  m_global_label_cache[name].push_back( subgraph );
902  break;
903  }
904  default:
905  wxLogTrace( "CONN", "Unexpected strong driver %s",
907  break;
908  }
909  }
910  }
911 
912  // Generate subgraphs for invisible power pins. These will be merged with other subgraphs
913  // on the same sheet in the next loop.
914 
915  std::unordered_map<int, CONNECTION_SUBGRAPH*> invisible_pin_subgraphs;
916 
917  for( const auto& it : m_invisible_power_pins )
918  {
919  SCH_SHEET_PATH sheet = it.first;
920  SCH_PIN* pin = it.second;
921 
922  if( !pin->ConnectedItems( sheet ).empty() && !pin->GetLibPin()->GetParent()->IsPower() )
923  {
924  // ERC will warn about this: user has wired up an invisible pin
925  continue;
926  }
927 
928  SCH_CONNECTION* connection = pin->Connection( sheet );
929 
930  if( !connection )
931  connection = pin->InitializeConnection( sheet, this );
932 
933  // If this pin already has a subgraph, don't need to process
934  if( connection->SubgraphCode() > 0 )
935  continue;
936 
937  connection->SetName( pin->GetName() );
938 
939  int code = assignNewNetCode( *connection );
940 
941  connection->SetNetCode( code );
942 
943  CONNECTION_SUBGRAPH* subgraph;
944 
945  if( invisible_pin_subgraphs.count( code ) )
946  {
947  subgraph = invisible_pin_subgraphs.at( code );
948  subgraph->AddItem( pin );
949  }
950  else
951  {
952  subgraph = new CONNECTION_SUBGRAPH( this );
953 
954  subgraph->m_code = m_last_subgraph_code++;
955  subgraph->m_sheet = sheet;
956 
957  subgraph->AddItem( pin );
958  subgraph->ResolveDrivers();
959 
960  auto key = std::make_pair( subgraph->GetNetName(), code );
961  m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
962  m_subgraphs.push_back( subgraph );
963  m_driver_subgraphs.push_back( subgraph );
964 
965  invisible_pin_subgraphs[code] = subgraph;
966  }
967 
968  connection->SetSubgraphCode( subgraph->m_code );
969  }
970 
971  for( auto it : invisible_pin_subgraphs )
972  it.second->UpdateItemConnections();
973 
974  // Here we do all the local (sheet) processing of each subgraph, including assigning net
975  // codes, merging subgraphs together that use label connections, etc.
976 
977  // Cache remaining valid subgraphs by sheet path
978  for( auto subgraph : m_driver_subgraphs )
979  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
980 
981  std::unordered_set<CONNECTION_SUBGRAPH*> invalidated_subgraphs;
982 
983  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
984  {
985  if( subgraph->m_absorbed )
986  continue;
987 
988  SCH_CONNECTION* connection = subgraph->m_driver_connection;
989  SCH_SHEET_PATH sheet = subgraph->m_sheet;
990  wxString name = connection->Name();
991 
992  // Test subgraphs with weak drivers for net name conflicts and fix them
993  unsigned suffix = 1;
994 
995  auto create_new_name = [&] ( SCH_CONNECTION* aConn, wxString aName ) -> wxString
996  {
997  wxString new_name = wxString::Format( "%s_%u", aName, suffix );
998  aConn->SetSuffix( wxString::Format( "_%u", suffix ) );
999  suffix++;
1000  return new_name;
1001  };
1002 
1003  if( !subgraph->m_strong_driver )
1004  {
1005  auto& vec = m_net_name_to_subgraphs_map.at( name );
1006 
1007  if( vec.size() > 1 )
1008  {
1009  wxString new_name = create_new_name( connection, name );
1010 
1011  while( m_net_name_to_subgraphs_map.count( new_name ) )
1012  new_name = create_new_name( connection, name );
1013 
1014  wxLogTrace( "CONN", "%ld (%s) is weakly driven and not unique. Changing to %s.",
1015  subgraph->m_code, name, new_name );
1016 
1017  vec.erase( std::remove( vec.begin(), vec.end(), subgraph ), vec.end() );
1018 
1019  m_net_name_to_subgraphs_map[new_name].emplace_back( subgraph );
1020 
1021  name = new_name;
1022 
1023  subgraph->UpdateItemConnections();
1024  }
1025  else
1026  {
1027  // If there is no conflict, promote sheet pins to be strong drivers so that they
1028  // will be considered below for propagation/merging.
1029 
1030  // It is possible for this to generate a conflict if the sheet pin has the same
1031  // name as a global label on the same sheet, because global merging will then treat
1032  // this subgraph as if it had a matching local label. So, for those cases, we
1033  // don't apply this promotion
1034 
1035  if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1036  {
1037  bool conflict = false;
1038  wxString global_name = connection->Name( true );
1039  size_t count = m_net_name_to_subgraphs_map.count( global_name );
1040 
1041  if( count )
1042  {
1043  // A global will conflict if it is on the same sheet as this subgraph, since
1044  // it would be connected by implicit local label linking
1045  auto& candidates = m_net_name_to_subgraphs_map.at( global_name );
1046 
1047  for( const auto& candidate : candidates )
1048  {
1049  if( candidate->m_sheet == sheet )
1050  conflict = true;
1051  }
1052  }
1053 
1054  if( conflict )
1055  {
1056  wxLogTrace( "CONN",
1057  "%ld (%s) skipped for promotion due to potential conflict",
1058  subgraph->m_code, name );
1059  }
1060  else
1061  {
1062  wxLogTrace( "CONN",
1063  "%ld (%s) weakly driven by unique sheet pin %s, promoting",
1064  subgraph->m_code, name,
1066 
1067  subgraph->m_strong_driver = true;
1068  }
1069  }
1070  }
1071  }
1072 
1073  // Assign net codes
1074 
1075  if( connection->IsBus() )
1076  {
1077  int code = -1;
1078 
1079  if( m_bus_name_to_code_map.count( name ) )
1080  {
1081  code = m_bus_name_to_code_map.at( name );
1082  }
1083  else
1084  {
1085  code = m_last_bus_code++;
1086  m_bus_name_to_code_map[ name ] = code;
1087  }
1088 
1089  connection->SetBusCode( code );
1090  assignNetCodesToBus( connection );
1091  }
1092  else
1093  {
1094  assignNewNetCode( *connection );
1095  }
1096 
1097  subgraph->UpdateItemConnections();
1098 
1099  // Reset the flag for the next loop below
1100  subgraph->m_dirty = true;
1101 
1102  // Next, we merge together subgraphs that have label connections, and create
1103  // neighbor links for subgraphs that are part of a bus on the same sheet.
1104  // For merging, we consider each possible strong driver.
1105 
1106  // If this subgraph doesn't have a strong driver, let's skip it, since there is no
1107  // way it will be merged with anything.
1108 
1109  if( !subgraph->m_strong_driver )
1110  continue;
1111 
1112  // candidate_subgraphs will contain each valid, non-bus subgraph on the same sheet
1113  // as the subgraph we are considering that has a strong driver.
1114  // Weakly driven subgraphs are not considered since they will never be absorbed or
1115  // form neighbor links.
1116 
1117  std::vector<CONNECTION_SUBGRAPH*> candidate_subgraphs;
1118  std::copy_if( m_sheet_to_subgraphs_map[ subgraph->m_sheet ].begin(),
1119  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].end(),
1120  std::back_inserter( candidate_subgraphs ),
1121  [&] ( const CONNECTION_SUBGRAPH* candidate )
1122  {
1123  return ( !candidate->m_absorbed &&
1124  candidate->m_strong_driver &&
1125  candidate != subgraph );
1126  } );
1127 
1128  // This is a list of connections on the current subgraph to compare to the
1129  // drivers of each candidate subgraph. If the current subgraph is a bus,
1130  // we should consider each bus member.
1131  std::vector< std::shared_ptr<SCH_CONNECTION> > connections_to_check;
1132 
1133  // Also check the main driving connection
1134  connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
1135 
1136  auto add_connections_to_check = [&] ( CONNECTION_SUBGRAPH* aSubgraph ) {
1137  for( SCH_ITEM* possible_driver : aSubgraph->m_items )
1138  {
1139  if( possible_driver == aSubgraph->m_driver )
1140  continue;
1141 
1142  auto c = getDefaultConnection( possible_driver, aSubgraph->m_sheet );
1143 
1144  if( c )
1145  {
1146  if( c->Type() != aSubgraph->m_driver_connection->Type() )
1147  continue;
1148 
1149  if( c->Name( true ) == aSubgraph->m_driver_connection->Name( true ) )
1150  continue;
1151 
1152  connections_to_check.push_back( c );
1153  wxLogTrace( "CONN", "%lu (%s): Adding secondary driver %s", aSubgraph->m_code,
1154  aSubgraph->m_driver_connection->Name( true ), c->Name( true ) );
1155  }
1156  }
1157  };
1158 
1159  // Now add other strong drivers
1160  // The actual connection attached to these items will have been overwritten
1161  // by the chosen driver of the subgraph, so we need to create a dummy connection
1162  add_connections_to_check( subgraph );
1163 
1164  for( unsigned i = 0; i < connections_to_check.size(); i++ )
1165  {
1166  auto member = connections_to_check[i];
1167 
1168  if( member->IsBus() )
1169  {
1170  connections_to_check.insert( connections_to_check.end(),
1171  member->Members().begin(),
1172  member->Members().end() );
1173  }
1174 
1175  wxString test_name = member->Name( true );
1176 
1177  for( auto candidate : candidate_subgraphs )
1178  {
1179  if( candidate->m_absorbed )
1180  continue;
1181 
1182  bool match = false;
1183 
1184  if( candidate->m_driver_connection->Name( true ) == test_name )
1185  {
1186  match = true;
1187  }
1188  else
1189  {
1190  if( !candidate->m_multiple_drivers )
1191  continue;
1192 
1193  for( SCH_ITEM *driver : candidate->m_drivers )
1194  {
1195  if( driver == candidate->m_driver )
1196  continue;
1197 
1198  // Sheet pins are not candidates for merging
1199  if( driver->Type() == SCH_SHEET_PIN_T )
1200  continue;
1201 
1202  if( driver->Type() == SCH_PIN_T )
1203  {
1204  auto pin = static_cast<SCH_PIN*>( driver );
1205 
1206  if( pin->IsPowerConnection() && pin->GetName() == test_name )
1207  {
1208  match = true;
1209  break;
1210  }
1211  }
1212  else
1213  {
1214  wxASSERT( driver->Type() == SCH_LABEL_T ||
1215  driver->Type() == SCH_GLOBAL_LABEL_T ||
1216  driver->Type() == SCH_HIER_LABEL_T );
1217 
1218  auto text = static_cast<SCH_TEXT*>( driver );
1219 
1220  if( EscapeString( text->GetShownText(), CTX_NETNAME ) == test_name )
1221  {
1222  match = true;
1223  break;
1224  }
1225  }
1226  }
1227  }
1228 
1229  if( match )
1230  {
1231  if( connection->IsBus() && candidate->m_driver_connection->IsNet() )
1232  {
1233  wxLogTrace( "CONN", "%lu (%s) has bus child %lu (%s)", subgraph->m_code,
1234  connection->Name(), candidate->m_code, member->Name() );
1235 
1236  subgraph->m_bus_neighbors[member].insert( candidate );
1237  candidate->m_bus_parents[member].insert( subgraph );
1238  }
1239  else
1240  {
1241  wxLogTrace( "CONN", "%lu (%s) absorbs neighbor %lu (%s)",
1242  subgraph->m_code, connection->Name(),
1243  candidate->m_code, candidate->m_driver_connection->Name() );
1244 
1245  // Candidate may have other non-chosen drivers we need to follow
1246  add_connections_to_check( candidate );
1247 
1248  subgraph->Absorb( candidate );
1249  invalidated_subgraphs.insert( subgraph );
1250  }
1251  }
1252  }
1253  }
1254  }
1255 
1256  // Update any subgraph that was invalidated above
1257  for( auto subgraph : invalidated_subgraphs )
1258  {
1259  if( subgraph->m_absorbed )
1260  continue;
1261 
1262  subgraph->ResolveDrivers();
1263 
1264  if( subgraph->m_driver_connection->IsBus() )
1266  else
1267  assignNewNetCode( *subgraph->m_driver_connection );
1268 
1269  subgraph->UpdateItemConnections();
1270 
1271  wxLogTrace( "CONN", "Re-resolving drivers for %lu (%s)", subgraph->m_code,
1272  subgraph->m_driver_connection->Name() );
1273  }
1274 
1275  // Absorbed subgraphs should no longer be considered
1276  m_driver_subgraphs.erase( std::remove_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1277  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1278  {
1279  return candidate->m_absorbed;
1280  } ),
1281  m_driver_subgraphs.end() );
1282 
1283  // Store global subgraphs for later reference
1284  std::vector<CONNECTION_SUBGRAPH*> global_subgraphs;
1285  std::copy_if( m_driver_subgraphs.begin(), m_driver_subgraphs.end(),
1286  std::back_inserter( global_subgraphs ),
1287  [&] ( const CONNECTION_SUBGRAPH* candidate ) -> bool
1288  {
1289  return !candidate->m_local_driver;
1290  } );
1291 
1292  // Recache remaining valid subgraphs by sheet path
1293  m_sheet_to_subgraphs_map.clear();
1294  for( auto subgraph : m_driver_subgraphs )
1295  m_sheet_to_subgraphs_map[ subgraph->m_sheet ].emplace_back( subgraph );
1296 
1297  // Next time through the subgraphs, we do some post-processing to handle things like
1298  // connecting bus members to their neighboring subgraphs, and then propagate connections
1299  // through the hierarchy
1300 
1301  for( auto subgraph : m_driver_subgraphs )
1302  {
1303  if( !subgraph->m_dirty )
1304  continue;
1305 
1306  // For subgraphs that are driven by a global (power port or label) and have more
1307  // than one global driver, we need to seek out other subgraphs driven by the
1308  // same name as the non-chosen driver and update them to match the chosen one.
1309 
1310  if( !subgraph->m_local_driver && subgraph->m_multiple_drivers )
1311  {
1312  for( SCH_ITEM* driver : subgraph->m_drivers )
1313  {
1314  if( driver == subgraph->m_driver )
1315  continue;
1316 
1317  wxString secondary_name = subgraph->GetNameForDriver( driver );
1318 
1319  if( secondary_name == subgraph->m_driver_connection->Name() )
1320  continue;
1321 
1322  bool secondary_is_global = CONNECTION_SUBGRAPH::GetDriverPriority( driver )
1324 
1325  for( CONNECTION_SUBGRAPH* candidate : global_subgraphs )
1326  {
1327  if( candidate == subgraph )
1328  continue;
1329 
1330  if( !secondary_is_global && candidate->m_sheet != subgraph->m_sheet )
1331  continue;
1332 
1333  SCH_CONNECTION* conn = candidate->m_driver_connection;
1334 
1335  if( conn->Name() == secondary_name )
1336  {
1337  wxLogTrace( "CONN", "Global %lu (%s) promoted to %s", candidate->m_code,
1338  conn->Name(), subgraph->m_driver_connection->Name() );
1339 
1340  conn->Clone( *subgraph->m_driver_connection );
1341  candidate->UpdateItemConnections();
1342 
1343  candidate->m_dirty = false;
1344  }
1345  }
1346  }
1347  }
1348 
1349  // This call will handle descending the hierarchy and updating child subgraphs
1350  propagateToNeighbors( subgraph );
1351  }
1352 
1353  // Handle buses that have been linked together somewhere by member (net) connections.
1354  // This feels a bit hacky, perhaps this algorithm should be revisited in the future.
1355 
1356  // For net subgraphs that have more than one bus parent, we need to ensure that those
1357  // buses are linked together in the final netlist. The final name of each bus might not
1358  // match the local name that was used to establish the parent-child relationship, because
1359  // the bus may have been renamed by a hierarchical connection. So, for each of these cases,
1360  // we need to identify the appropriate bus members to link together (and their final names),
1361  // and then update all instances of the old name in the hierarchy.
1362 
1363  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1364  {
1365  if( subgraph->m_bus_parents.size() < 2 )
1366  continue;
1367 
1368  SCH_CONNECTION* conn = subgraph->m_driver_connection;
1369 
1370  wxLogTrace( "CONN", "%lu (%s) has multiple bus parents", subgraph->m_code, conn->Name() );
1371 
1372  wxASSERT( conn->IsNet() );
1373 
1374  for( const auto& it : subgraph->m_bus_parents )
1375  {
1376  SCH_CONNECTION* link_member = it.first.get();
1377 
1378  for( CONNECTION_SUBGRAPH* parent : it.second )
1379  {
1380  while( parent->m_absorbed )
1381  parent = parent->m_absorbed_by;
1382 
1383  SCH_CONNECTION* match = matchBusMember( parent->m_driver_connection, link_member );
1384 
1385  if( !match )
1386  {
1387  wxLogTrace( "CONN", "Warning: could not match %s inside %lu (%s)", conn->Name(),
1388  parent->m_code, parent->m_driver_connection->Name() );
1389  continue;
1390  }
1391 
1392  if( conn->Name() != match->Name() )
1393  {
1394  wxString old_name = match->Name();
1395 
1396  wxLogTrace( "CONN", "Updating %lu (%s) member %s to %s", parent->m_code,
1397  parent->m_driver_connection->Name(), old_name, conn->Name() );
1398 
1399  match->Clone( *conn );
1400 
1401  if( !m_net_name_to_subgraphs_map.count( old_name ) )
1402  continue;
1403 
1404  for( CONNECTION_SUBGRAPH* old_sg : m_net_name_to_subgraphs_map.at( old_name ) )
1405  {
1406  while( old_sg->m_absorbed )
1407  old_sg = old_sg->m_absorbed_by;
1408 
1409  old_sg->m_driver_connection->Clone( *conn );
1410  old_sg->UpdateItemConnections();
1411  }
1412  }
1413  }
1414  }
1415  }
1416 
1419 
1420  for( CONNECTION_SUBGRAPH* subgraph : m_driver_subgraphs )
1421  {
1422  // Every driven subgraph should have been marked by now
1423  if( subgraph->m_dirty )
1424  {
1425  // TODO(JE) this should be caught by hierarchical sheet port/pin ERC, check this
1426  // Reset to false so no complaints come up later
1427  subgraph->m_dirty = false;
1428  }
1429 
1430  if( subgraph->m_driver_connection->IsBus() )
1431  {
1432  // No other processing to do on buses
1433  continue;
1434  }
1435  else
1436  {
1437  // As a visual aid, we can check sheet pins that are driven by themselves to see
1438  // if they should be promoted to buses
1439 
1440  if( subgraph->m_driver->Type() == SCH_SHEET_PIN_T )
1441  {
1442  SCH_SHEET_PIN* pin = static_cast<SCH_SHEET_PIN*>( subgraph->m_driver );
1443 
1444  if( SCH_SHEET* sheet = pin->GetParent() )
1445  {
1446  wxString pinText = pin->GetText();
1447 
1448  for( auto item : sheet->GetScreen()->Items().OfType( SCH_HIER_LABEL_T ) )
1449  {
1450  auto label = static_cast<SCH_HIERLABEL*>( item );
1451 
1452  if( label->GetText() == pinText )
1453  {
1454  SCH_SHEET_PATH path = subgraph->m_sheet;
1455  path.push_back( sheet );
1456 
1457  SCH_CONNECTION* parent_conn = label->Connection( path );
1458 
1459  if( parent_conn && parent_conn->IsBus() )
1461 
1462  break;
1463  }
1464  }
1465 
1466  if( subgraph->m_driver_connection->IsBus() )
1467  continue;
1468  }
1469  }
1470  }
1471 
1472  auto key = std::make_pair( subgraph->GetNetName(),
1473  subgraph->m_driver_connection->NetCode() );
1474  m_net_code_to_subgraphs_map[ key ].push_back( subgraph );
1475 
1476  m_net_name_to_subgraphs_map[subgraph->m_driver_connection->Name()].push_back( subgraph );
1477  }
1478 
1479  // Clean up and deallocate stale subgraphs
1480  m_subgraphs.erase( std::remove_if( m_subgraphs.begin(), m_subgraphs.end(),
1481  [&]( const CONNECTION_SUBGRAPH* sg )
1482  {
1483  if( sg->m_absorbed )
1484  {
1485  delete sg;
1486  return true;
1487  }
1488  else
1489  {
1490  return false;
1491  }
1492  } ),
1493  m_subgraphs.end() );
1494 }
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:65
void propagateToNeighbors(CONNECTION_SUBGRAPH *aSubgraph)
Updates all neighbors of a subgraph with this one's connectivity info.
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:176
SCH_SHEET * GetParent() const
Get the parent sheet object of this sheet pin.
Definition: sch_sheet.h:168
SCH_SHEET_LIST GetSheets() const
Builds and returns an updated schematic hierarchy TODO: can this be cached?
Definition: schematic.h:92
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
const wxString & GetName() const
Definition: sch_pin.h:102
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...
LIB_PART * GetParent() const
Definition: lib_item.h:182
void Absorb(CONNECTION_SUBGRAPH *aOther)
Combines another subgraph on the same sheet into this one.
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!
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 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
SCHEMATIC * m_schematic
The schematic this graph represents.
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.
bool m_multiple_drivers
True if this subgraph contains more than one driver that should be shorted together in the netlist.
int NetCode() const
SCH_CONNECTION * InitializeConnection(const SCH_SHEET_PATH &aPath, CONNECTION_GRAPH *aGraph)
Creates a new connection object associated with this object.
Definition: sch_item.cpp:188
int assignNewNetCode(SCH_CONNECTION &aConnection)
Helper to assign a new net code to a connection.
void SetSubgraphCode(int aCode)
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
void AddItem(SCH_ITEM *aItem)
Adds a new item to the subgraph.
int SubgraphCode() const
bool m_strong_driver
True if the driver is "strong": a label or power object.
std::vector< SCH_ITEM * > m_drivers
const wxString & Name(bool aIgnoreSheet=false) const
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
SCH_SHEET_PATH.
void Clone(SCH_CONNECTION &aOther)
Copies connectivity information (but not parent) from another connection.
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
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:216
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
bool IsNet() const
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
bool IsPower() const
const char * name
Definition: DXF_plotter.cpp:60
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:205
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
void SetSuffix(const wxString &aSuffix)
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:50
std::map< wxString, int > m_bus_name_to_code_map
std::unordered_set< SCH_ITEM * > m_items
std::shared_ptr< SCH_CONNECTION > getDefaultConnection(SCH_ITEM *aItem, const SCH_SHEET_PATH &aSheet)
Builds a new default connection for the given item based on its properties.
bool IsPowerConnection() const
Definition: sch_pin.h:108
void SetType(CONNECTION_TYPE aType)
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
not connected (must be left open)
bool IsBus() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:187
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:126
void SetNetCode(int aCode)
wxString GetNetName() const
Returns the fully-qualified net name for this subgraph (if one exists)
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.
wxString GetNameForDriver(SCH_ITEM *aItem) const
Returns the candidate net name for a driver.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
void UpdateItemConnections()
Updates all items to match the driver connection.

References CONNECTION_SUBGRAPH::AddItem(), assignNetCodesToBus(), assignNewNetCode(), BUS, SCH_CONNECTION::ClearDirty(), SCH_CONNECTION::Clone(), SCH_CONNECTION::ConfigureFromLabel(), SCH_ITEM::ConnectedItems(), SCH_ITEM::Connection(), CTX_NETNAME, EscapeString(), Format(), getDefaultConnection(), CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_PIN::GetLibPin(), SCH_PIN::GetName(), CONNECTION_SUBGRAPH::GetNetName(), SCH_SHEET_PIN::GetParent(), LIB_ITEM::GetParent(), EDA_ITEM::GetSelectMenuText(), SCHEMATIC::GetSheets(), EDA_TEXT::GetText(), SCH_ITEM::InitializeConnection(), SCH_CONNECTION::IsBus(), SCH_CONNECTION::IsNet(), LIB_PART::IsPower(), CONNECTION_SUBGRAPH::m_absorbed, CONNECTION_SUBGRAPH::m_absorbed_by, m_bus_alias_cache, m_bus_name_to_code_map, CONNECTION_SUBGRAPH::m_code, CONNECTION_SUBGRAPH::m_driver_connection, m_driver_subgraphs, m_global_label_cache, m_invisible_power_pins, m_item_to_subgraph_map, m_items, m_last_bus_code, m_last_subgraph_code, m_local_label_cache, m_net_code_to_subgraphs_map, m_net_name_to_subgraphs_map, m_schematic, CONNECTION_SUBGRAPH::m_sheet, m_sheet_to_subgraphs_map, m_subgraphs, matchBusMember(), MILLIMETRES, name, SCH_CONNECTION::Name(), CONNECTION_SUBGRAPH::POWER_PIN, propagateToNeighbors(), PT_NC, SCH_SHEET_PATH::push_back(), CONNECTION_SUBGRAPH::ResolveDrivers(), SCH_BUS_WIRE_ENTRY_T, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LABEL_T, SCH_NO_CONNECT_T, SCH_PIN_T, SCH_SHEET_PIN_T, SCH_CONNECTION::SetBusCode(), SCH_CONNECTION::SetDriver(), SCH_CONNECTION::SetName(), SCH_CONNECTION::SetNetCode(), SCH_CONNECTION::SetSubgraphCode(), SCH_CONNECTION::SetSuffix(), SCH_CONNECTION::SetType(), SCH_CONNECTION::SubgraphCode(), and EDA_ITEM::Type().

Referenced by Recalculate().

◆ ercCheckBusToBusConflicts()

bool CONNECTION_GRAPH::ercCheckBusToBusConflicts ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for conflicting connections between two bus items.

For example, a labeled bus wire connected to a hierarchical sheet pin where the labeled bus doesn't contain any of the same bus members as the sheet pin

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2129 of file connection_graph.cpp.

2130 {
2131  wxString msg;
2132  auto sheet = aSubgraph->m_sheet;
2133  auto screen = sheet.LastScreen();
2134 
2135  SCH_ITEM* label = nullptr;
2136  SCH_ITEM* port = nullptr;
2137 
2138  for( auto item : aSubgraph->m_items )
2139  {
2140  switch( item->Type() )
2141  {
2142  case SCH_TEXT_T:
2143  case SCH_GLOBAL_LABEL_T:
2144  {
2145  if( !label && item->Connection( sheet )->IsBus() )
2146  label = item;
2147  break;
2148  }
2149 
2150  case SCH_SHEET_PIN_T:
2151  case SCH_HIER_LABEL_T:
2152  {
2153  if( !port && item->Connection( sheet )->IsBus() )
2154  port = item;
2155  break;
2156  }
2157 
2158  default:
2159  break;
2160  }
2161  }
2162 
2163  if( label && port )
2164  {
2165  bool match = false;
2166 
2167  for( const auto& member : label->Connection( sheet )->Members() )
2168  {
2169  for( const auto& test : port->Connection( sheet )->Members() )
2170  {
2171  if( test != member && member->Name() == test->Name() )
2172  {
2173  match = true;
2174  break;
2175  }
2176  }
2177 
2178  if( match )
2179  break;
2180  }
2181 
2182  if( !match )
2183  {
2185  ercItem->SetItems( label, port );
2186 
2187  SCH_MARKER* marker = new SCH_MARKER( ercItem, label->GetPosition() );
2188  screen->Append( marker );
2189 
2190  return false;
2191  }
2192  }
2193 
2194  return true;
2195 }
SCH_SHEET_PATH m_sheet
void SetItems(EDA_ITEM *aItem, EDA_ITEM *bItem=nullptr, EDA_ITEM *cItem=nullptr, EDA_ITEM *dItem=nullptr)
Definition: rc_item.h:119
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
std::vector< SCH_ITEM * > m_items
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
static ERC_ITEM * Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:144
SCH_SCREEN * LastScreen()
Function LastScreen.
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:187

References SCH_ITEM::Connection(), ERC_ITEM::Create(), ERCE_BUS_TO_BUS_CONFLICT, EDA_ITEM::GetPosition(), SCH_CONNECTION::IsBus(), SCH_SHEET_PATH::LastScreen(), CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, SCH_CONNECTION::Members(), SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_SHEET_PIN_T, SCH_TEXT_T, and RC_ITEM::SetItems().

Referenced by RunERC().

◆ ercCheckBusToBusEntryConflicts()

bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for conflicting bus entry to bus connections.

For example, a wire with label "A0" is connected to a bus labeled "D[8..0]"

Will also check for mistakes related to bus group names, for example: A bus group named "USB{DP DM}" should have bus entry connections like "USB.DP" but someone might accidentally just enter "DP"

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2198 of file connection_graph.cpp.

2199 {
2200  bool conflict = false;
2201  auto sheet = aSubgraph->m_sheet;
2202  auto screen = sheet.LastScreen();
2203 
2204  SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
2205  SCH_ITEM* bus_wire = nullptr;
2206 
2207  for( auto item : aSubgraph->m_items )
2208  {
2209  switch( item->Type() )
2210  {
2211  case SCH_BUS_WIRE_ENTRY_T:
2212  {
2213  if( !bus_entry )
2214  bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
2215  break;
2216  }
2217 
2218  default:
2219  break;
2220  }
2221  }
2222 
2223  if( bus_entry && bus_entry->m_connected_bus_item )
2224  {
2225  bus_wire = bus_entry->m_connected_bus_item;
2226 
2227  wxASSERT( bus_wire->Type() == SCH_LINE_T );
2228 
2229  // In some cases, the connection list (SCH_CONNECTION*) can be null.
2230  // Skip null connections.
2231  if( bus_entry->Connection( sheet ) && bus_wire->Type() == SCH_LINE_T
2232  && bus_wire->Connection( sheet ) )
2233  {
2234  conflict = true;
2235 
2236  auto test_name = bus_entry->Connection( sheet )->Name( true );
2237 
2238  for( const auto& member : bus_wire->Connection( sheet )->Members() )
2239  {
2240  if( member->Type() == CONNECTION_TYPE::BUS )
2241  {
2242  for( const auto& sub_member : member->Members() )
2243  {
2244  if( sub_member->Name( true ) == test_name )
2245  conflict = false;
2246  }
2247  }
2248  else if( member->Name( true ) == test_name )
2249  {
2250  conflict = false;
2251  }
2252  }
2253  }
2254  }
2255 
2256  // Don't report warnings if this bus member has been overridden by a higher priority power pin
2257  // or global label
2258  if( conflict && CONNECTION_SUBGRAPH::GetDriverPriority( aSubgraph->m_driver )
2260  conflict = false;
2261 
2262  if( conflict )
2263  {
2265  ercItem->SetItems( bus_entry, bus_wire );
2266 
2267  SCH_MARKER* marker = new SCH_MARKER( ercItem, bus_entry->GetPosition() );
2268  screen->Append( marker );
2269 
2270  return false;
2271  }
2272 
2273  return true;
2274 }
SCH_SHEET_PATH m_sheet
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 ...
void SetItems(EDA_ITEM *aItem, EDA_ITEM *bItem=nullptr, EDA_ITEM *cItem=nullptr, EDA_ITEM *dItem=nullptr)
Definition: rc_item.h:119
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Returns the priority (higher is more important) of a candidate driver.
std::vector< SCH_ITEM * > m_items
const wxString & Name(bool aIgnoreSheet=false) const
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
static ERC_ITEM * Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:144
SCH_SCREEN * LastScreen()
Function LastScreen.
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
Class for a wire to bus entry.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:187
This item represents a bus vector.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
wxPoint GetPosition() const override

References BUS, SCH_ITEM::Connection(), ERC_ITEM::Create(), ERCE_BUS_ENTRY_CONFLICT, CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_BUS_ENTRY_BASE::GetPosition(), SCH_SHEET_PATH::LastScreen(), SCH_BUS_WIRE_ENTRY::m_connected_bus_item, CONNECTION_SUBGRAPH::m_driver, CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, SCH_CONNECTION::Members(), SCH_CONNECTION::Name(), CONNECTION_SUBGRAPH::POWER_PIN, SCH_BUS_WIRE_ENTRY_T, SCH_LINE_T, RC_ITEM::SetItems(), and EDA_ITEM::Type().

Referenced by RunERC().

◆ ercCheckBusToNetConflicts()

bool CONNECTION_GRAPH::ercCheckBusToNetConflicts ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for conflicting connections between net and bus labels.

For example, a net wire connected to a bus port/pin, or vice versa

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2073 of file connection_graph.cpp.

2074 {
2075  auto sheet = aSubgraph->m_sheet;
2076  auto screen = sheet.LastScreen();
2077 
2078  SCH_ITEM* net_item = nullptr;
2079  SCH_ITEM* bus_item = nullptr;
2080  SCH_CONNECTION conn( this );
2081 
2082  for( auto item : aSubgraph->m_items )
2083  {
2084  switch( item->Type() )
2085  {
2086  case SCH_LINE_T:
2087  {
2088  if( item->GetLayer() == LAYER_BUS )
2089  bus_item = ( !bus_item ) ? item : bus_item;
2090  else
2091  net_item = ( !net_item ) ? item : net_item;
2092  break;
2093  }
2094 
2095  case SCH_GLOBAL_LABEL_T:
2096  case SCH_SHEET_PIN_T:
2097  case SCH_HIER_LABEL_T:
2098  {
2099  SCH_TEXT* text = static_cast<SCH_TEXT*>( item );
2100  conn.ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
2101 
2102  if( conn.IsBus() )
2103  bus_item = ( !bus_item ) ? item : bus_item;
2104  else
2105  net_item = ( !net_item ) ? item : net_item;
2106  break;
2107  }
2108 
2109  default:
2110  break;
2111  }
2112  }
2113 
2114  if( net_item && bus_item )
2115  {
2117  ercItem->SetItems( net_item, bus_item );
2118 
2119  SCH_MARKER* marker = new SCH_MARKER( ercItem, net_item->GetPosition() );
2120  screen->Append( marker );
2121 
2122  return false;
2123  }
2124 
2125  return true;
2126 }
SCH_SHEET_PATH m_sheet
void SetItems(EDA_ITEM *aItem, EDA_ITEM *bItem=nullptr, EDA_ITEM *cItem=nullptr, EDA_ITEM *dItem=nullptr)
Definition: rc_item.h:119
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
std::vector< SCH_ITEM * > m_items
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_text.cpp:484
static ERC_ITEM * Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:144
SCH_SCREEN * LastScreen()
Function LastScreen.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
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:50
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:187

References SCH_CONNECTION::ConfigureFromLabel(), ERC_ITEM::Create(), CTX_NETNAME, ERCE_BUS_TO_NET_CONFLICT, EscapeString(), EDA_ITEM::GetPosition(), SCH_TEXT::GetShownText(), SCH_CONNECTION::IsBus(), SCH_SHEET_PATH::LastScreen(), LAYER_BUS, CONNECTION_SUBGRAPH::m_items, CONNECTION_SUBGRAPH::m_sheet, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LINE_T, SCH_SHEET_PIN_T, and RC_ITEM::SetItems().

Referenced by RunERC().

◆ ercCheckLabels()

bool CONNECTION_GRAPH::ercCheckLabels ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for proper connection of labels.

Labels should be connected to something

Parameters
aSubgraphis the subgraph to examine
aCheckGlobalLabelsis true if global labels should be checked for loneliness
Returns
true for no errors, false for errors

Definition at line 2397 of file connection_graph.cpp.

2398 {
2399  // Label connection rules:
2400  // Local labels are flagged if they don't connect to any pins and don't have a no-connect
2401  // Global labels are flagged if they appear only once, don't connect to any local labels,
2402  // and don't have a no-connect marker
2403 
2404  // So, if there is a no-connect, we will never generate a warning here
2405  if( aSubgraph->m_no_connect )
2406  return true;
2407 
2408  SCH_TEXT* text = nullptr;
2409  bool has_other_connections = false;
2410 
2411  for( auto item : aSubgraph->m_items )
2412  {
2413  switch( item->Type() )
2414  {
2415  case SCH_LABEL_T:
2416  case SCH_GLOBAL_LABEL_T:
2417  case SCH_HIER_LABEL_T:
2418  text = static_cast<SCH_TEXT*>( item );
2419  break;
2420 
2421  case SCH_PIN_T:
2422  case SCH_SHEET_PIN_T:
2423  has_other_connections = true;
2424  break;
2425 
2426  default:
2427  break;
2428  }
2429  }
2430 
2431  if( !text )
2432  return true;
2433 
2434  bool is_global = text->Type() == SCH_GLOBAL_LABEL_T;
2435 
2436  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2437 
2438  // Global label check can be disabled independently
2439  if( !m_schematic->ErcSettings().IsTestEnabled( ERCE_GLOBLABEL ) && is_global )
2440  return true;
2441 
2442  wxString name = EscapeString( text->GetShownText(), CTX_NETNAME );
2443 
2444  if( is_global )
2445  {
2446  // This will be set to true if the global is connected to a pin above, but we
2447  // want to reset this to false so that globals get flagged if they only have a
2448  // single instance
2449  has_other_connections = false;
2450 
2451  if( m_net_name_to_subgraphs_map.count( name )
2452  && m_net_name_to_subgraphs_map.at( name ).size() > 1 )
2453  has_other_connections = true;
2454  }
2455  else if( text->Type() == SCH_HIER_LABEL_T )
2456  {
2457  // For a hier label, check if the parent pin is connected
2458  if( aSubgraph->m_hier_parent &&
2459  ( aSubgraph->m_hier_parent->m_strong_driver ||
2460  aSubgraph->m_hier_parent->m_drivers.size() > 1))
2461  {
2462  // For now, a simple check: if there is more than one driver, the parent is probably
2463  // connected elsewhere (because at least one driver will be the hier pin itself)
2464  has_other_connections = true;
2465  }
2466  }
2467  else
2468  {
2469  auto pair = std::make_pair( aSubgraph->m_sheet, name );
2470 
2471  if( m_local_label_cache.count( pair ) && m_local_label_cache.at( pair ).size() > 1 )
2472  has_other_connections = true;
2473  }
2474 
2475  if( !has_other_connections )
2476  {
2478  ercItem->SetItems( text );
2479 
2480  SCH_MARKER* marker = new SCH_MARKER( ercItem, text->GetPosition() );
2481  aSubgraph->m_sheet.LastScreen()->Append( marker );
2482 
2483  return false;
2484  }
2485 
2486  return true;
2487 }
SCH_SHEET_PATH m_sheet
void SetItems(EDA_ITEM *aItem, EDA_ITEM *bItem=nullptr, EDA_ITEM *cItem=nullptr, EDA_ITEM *dItem=nullptr)
Definition: rc_item.h:119
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:114
SCHEMATIC * m_schematic
The schematic this graph represents.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
std::vector< SCH_ITEM * > m_items
bool m_strong_driver
True if the driver is "strong": a label or power object.
std::vector< SCH_ITEM * > m_drivers
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_text.cpp:484
wxPoint GetPosition() const override
Definition: sch_text.h:313
static ERC_ITEM * Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:144
SCH_SCREEN * LastScreen()
Function LastScreen.
const char * name
Definition: DXF_plotter.cpp:60
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:133
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:50
CONNECTION_SUBGRAPH * m_hier_parent
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:145
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
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193

References SCH_SCREEN::Append(), ERC_ITEM::Create(), CTX_NETNAME, ERCE_GLOBLABEL, ERCE_LABEL_NOT_CONNECTED, SCHEMATIC::ErcSettings(), EscapeString(), SCH_TEXT::GetPosition(), SCH_TEXT::GetShownText(), ERC_SETTINGS::IsTestEnabled(), SCH_SHEET_PATH::LastScreen(), CONNECTION_SUBGRAPH::m_drivers, CONNECTION_SUBGRAPH::m_hier_parent, CONNECTION_SUBGRAPH::m_items, m_local_label_cache, m_net_name_to_subgraphs_map, CONNECTION_SUBGRAPH::m_no_connect, m_schematic, CONNECTION_SUBGRAPH::m_sheet, CONNECTION_SUBGRAPH::m_strong_driver, name, SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LABEL_T, SCH_PIN_T, SCH_SHEET_PIN_T, RC_ITEM::SetItems(), and EDA_ITEM::Type().

Referenced by RunERC().

◆ ercCheckNoConnects()

bool CONNECTION_GRAPH::ercCheckNoConnects ( const CONNECTION_SUBGRAPH aSubgraph)
private

Checks one subgraph for proper presence or absence of no-connect symbols.

A pin with a no-connect symbol should not have any connections A pin without a no-connect symbol should have at least one connection

Parameters
aSubgraphis the subgraph to examine
Returns
true for no errors, false for errors

Definition at line 2278 of file connection_graph.cpp.

2279 {
2280  wxString msg;
2281  auto sheet = aSubgraph->m_sheet;
2282  auto screen = sheet.LastScreen();
2283 
2284  if( aSubgraph->m_no_connect != nullptr )
2285  {
2286  bool has_invalid_items = false;
2287  bool has_other_items = false;
2288  SCH_PIN* pin = nullptr;
2289  std::vector<SCH_ITEM*> invalid_items;
2290 
2291  // Any subgraph that contains both a pin and a no-connect should not
2292  // contain any other driving items.
2293 
2294  for( auto item : aSubgraph->m_items )
2295  {
2296  switch( item->Type() )
2297  {
2298  case SCH_PIN_T:
2299  pin = static_cast<SCH_PIN*>( item );
2300  has_other_items = true;
2301  break;
2302 
2303  case SCH_LINE_T:
2304  case SCH_JUNCTION_T:
2305  case SCH_NO_CONNECT_T:
2306  break;
2307 
2308  default:
2309  has_invalid_items = true;
2310  has_other_items = true;
2311  invalid_items.push_back( item );
2312  }
2313  }
2314 
2315  if( pin && has_invalid_items )
2316  {
2318  ercItem->SetItems( pin );
2319 
2320  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2321  screen->Append( marker );
2322 
2323  return false;
2324  }
2325 
2326  if( !has_other_items )
2327  {
2329  ercItem->SetItems( aSubgraph->m_no_connect );
2330 
2331  SCH_MARKER* marker = new SCH_MARKER( ercItem, aSubgraph->m_no_connect->GetPosition() );
2332  screen->Append( marker );
2333 
2334  return false;
2335  }
2336  }
2337  else
2338  {
2339  bool has_other_connections = false;
2340  SCH_PIN* pin = nullptr;
2341 
2342  // Any subgraph that lacks a no-connect and contains a pin should also
2343  // contain at least one other connectable item.
2344 
2345  for( auto item : aSubgraph->m_items )
2346  {
2347  switch( item->Type() )
2348  {
2349  case SCH_PIN_T:
2350  if( !pin )
2351  pin = static_cast<SCH_PIN*>( item );
2352  else
2353  has_other_connections = true;
2354 
2355  break;
2356 
2357  default:
2358  if( item->IsConnectable() )
2359  has_other_connections = true;
2360 
2361  break;
2362  }
2363  }
2364 
2365  // Check if invisible power pins connect to anything else.
2366  // Note this won't catch a component with multiple invisible power pins but these don't
2367  // connect to any other net; maybe that should be added as a further optional ERC check?
2368 
2369  if( pin && !has_other_connections && pin->IsPowerConnection() && !pin->IsVisible() )
2370  {
2371  wxString name = pin->Connection( sheet )->Name();
2372  wxString local_name = pin->Connection( sheet )->Name( true );
2373 
2374  if( m_global_label_cache.count( name ) ||
2375  ( m_local_label_cache.count( std::make_pair( sheet, local_name ) ) ) )
2376  {
2377  has_other_connections = true;
2378  }
2379  }
2380 
2381  if( pin && !has_other_connections && pin->GetType() != ELECTRICAL_PINTYPE::PT_NC )
2382  {
2384  ercItem->SetItems( pin );
2385 
2386  SCH_MARKER* marker = new SCH_MARKER( ercItem, pin->GetTransformedPosition() );
2387  screen->Append( marker );
2388 
2389  return false;
2390  }
2391  }
2392 
2393  return true;
2394 }
SCH_SHEET_PATH m_sheet
bool IsVisible() const
Definition: sch_pin.h:100
void SetItems(EDA_ITEM *aItem, EDA_ITEM *bItem=nullptr, EDA_ITEM *cItem=nullptr, EDA_ITEM *dItem=nullptr)
Definition: rc_item.h:119
virtual wxPoint GetPosition() const
Definition: base_struct.h:337
std::vector< SCH_ITEM * > m_items
const wxString & Name(bool aIgnoreSheet=false) const
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
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
static ERC_ITEM * Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition: erc_item.cpp:144
SCH_SCREEN * LastScreen()
Function LastScreen.
const char * name
Definition: DXF_plotter.cpp:60
ELECTRICAL_PINTYPE GetType() const
Definition: sch_pin.h:106
bool IsPowerConnection() const
Definition: sch_pin.h:108
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
not connected (must be left open)
wxPoint GetTransformedPosition() const
Returns the pin's position in global coordinates.
Definition: sch_pin.cpp:116

References SCH_ITEM::Connection(), ERC_ITEM::Create(), ERCE_NOCONNECT_CONNECTED, ERCE_NOCONNECT_NOT_CONNECTED, ERCE_PIN_NOT_CONNECTED, EDA_ITEM::GetPosition(), SCH_PIN::GetTransformedPosition(), SCH_PIN::GetType(), SCH_PIN::IsPowerConnection(), SCH_PIN::IsVisible(), SCH_SHEET_PATH::LastScreen(), m_global_label_cache, CONNECTION_SUBGRAPH::m_items, m_local_label_cache, CONNECTION_SUBGRAPH::m_no_connect, CONNECTION_SUBGRAPH::m_sheet, name, SCH_CONNECTION::Name(), PT_NC, SCH_JUNCTION_T, SCH_LINE_T, SCH_NO_CONNECT_T, SCH_PIN_T, and RC_ITEM::SetItems().

Referenced by RunERC().

◆ FindFirstSubgraphByName()

CONNECTION_SUBGRAPH * CONNECTION_GRAPH::FindFirstSubgraphByName ( const wxString &  aNetName)

Retrieves a subgraph for the given net name, if one exists.

Searches every sheet

Parameters
aNetNameis the full net name to search for
Returns
the subgraph matching the query, or nullptr if none is found

Definition at line 1994 of file connection_graph.cpp.

1995 {
1996  if( !m_net_name_to_subgraphs_map.count( aNetName ) )
1997  return nullptr;
1998 
1999  wxASSERT( !m_net_name_to_subgraphs_map.at( aNetName ).empty() );
2000 
2001  return m_net_name_to_subgraphs_map.at( aNetName )[0];
2002 }
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map

References m_net_name_to_subgraphs_map.

◆ FindSubgraphByName()

CONNECTION_SUBGRAPH * CONNECTION_GRAPH::FindSubgraphByName ( const wxString &  aNetName,
const SCH_SHEET_PATH aPath 
)

Returns the subgraph for a given net name on a given sheet.

Parameters
aNetNameis the local net name to look for
aPathis a sheet path to look on
Returns
the subgraph matching the query, or nullptr if none is found

Definition at line 1975 of file connection_graph.cpp.

1977 {
1978  if( !m_net_name_to_subgraphs_map.count( aNetName ) )
1979  return nullptr;
1980 
1981  for( auto sg : m_net_name_to_subgraphs_map.at( aNetName ) )
1982  {
1983  // Cache is supposed to be valid by now
1984  wxASSERT( sg && !sg->m_absorbed && sg->m_driver_connection );
1985 
1986  if( sg->m_sheet == aPath && sg->m_driver_connection->Name() == aNetName )
1987  return sg;
1988  }
1989 
1990  return nullptr;
1991 }
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map

References m_net_name_to_subgraphs_map.

Referenced by NETLIST_EXPORTER::CreatePinList(), and NETLIST_EXPORTER::findAllUnitsOfComponent().

◆ GetBusAlias()

std::shared_ptr< BUS_ALIAS > CONNECTION_GRAPH::GetBusAlias ( const wxString &  aName)

Returns a bus alias pointer for the given name if it exists (from cache)

CONNECTION_GRAPH caches these, they are owned by the SCH_SCREEN that the alias was defined on. The cache is only used to update the graph.

Definition at line 1918 of file connection_graph.cpp.

1919 {
1920  if( m_bus_alias_cache.count( aName ) )
1921  return m_bus_alias_cache.at( aName );
1922 
1923  return nullptr;
1924 }
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache

References m_bus_alias_cache.

Referenced by SCH_CONNECTION::AppendInfoToMsgPanel(), and SCH_CONNECTION::ConfigureFromLabel().

◆ GetBusesNeedingMigration()

std::vector< const CONNECTION_SUBGRAPH * > CONNECTION_GRAPH::GetBusesNeedingMigration ( )

Determines which subgraphs have more than one conflicting bus label.

See also
DIALOG_MIGRATE_BUSES
Returns
a list of subgraphs that need migration

Definition at line 1927 of file connection_graph.cpp.

1928 {
1929  std::vector<const CONNECTION_SUBGRAPH*> ret;
1930 
1931  for( auto&& subgraph : m_subgraphs )
1932  {
1933  // Graph is supposed to be up-to-date before calling this
1934  wxASSERT( !subgraph->m_dirty );
1935 
1936  if( !subgraph->m_driver )
1937  continue;
1938 
1939  auto sheet = subgraph->m_sheet;
1940  auto connection = subgraph->m_driver->Connection( sheet );
1941 
1942  if( !connection->IsBus() )
1943  continue;
1944 
1945  auto labels = subgraph->GetBusLabels();
1946 
1947  if( labels.size() > 1 )
1948  {
1949  bool different = false;
1950  wxString first = static_cast<SCH_TEXT*>( labels.at( 0 ) )->GetShownText();
1951 
1952  for( unsigned i = 1; i < labels.size(); ++i )
1953  {
1954  if( static_cast<SCH_TEXT*>( labels.at( i ) )->GetShownText() != first )
1955  {
1956  different = true;
1957  break;
1958  }
1959  }
1960 
1961  if( !different )
1962  continue;
1963 
1964  wxLogTrace( "CONN", "SG %ld (%s) has multiple bus labels", subgraph->m_code,
1965  connection->Name() );
1966 
1967  ret.push_back( subgraph );
1968  }
1969  }
1970 
1971  return ret;
1972 }
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs

References m_subgraphs.

Referenced by DIALOG_MIGRATE_BUSES::loadGraphData().

◆ getDefaultConnection()

std::shared_ptr< SCH_CONNECTION > CONNECTION_GRAPH::getDefaultConnection ( SCH_ITEM aItem,
const SCH_SHEET_PATH aSheet 
)
private

Builds a new default connection for the given item based on its properties.

Handles strong drivers (power pins and labels) only

Parameters
aItemis an item that can generate a connection name
Returns
a connection generated from the item, or nullptr if item is not valid

Definition at line 1811 of file connection_graph.cpp.

1812 {
1813  auto c = std::shared_ptr<SCH_CONNECTION>( nullptr );
1814 
1815  switch( aItem->Type() )
1816  {
1817  case SCH_PIN_T:
1818  {
1819  auto pin = static_cast<SCH_PIN*>( aItem );
1820 
1821  if( pin->IsPowerConnection() )
1822  {
1823  c = std::make_shared<SCH_CONNECTION>( aItem, aSheet );
1824  c->SetGraph( this );
1825  c->ConfigureFromLabel( pin->GetName() );
1826  }
1827  break;
1828  }
1829 
1830  case SCH_GLOBAL_LABEL_T:
1831  case SCH_HIER_LABEL_T:
1832  case SCH_LABEL_T:
1833  {
1834  auto text = static_cast<SCH_TEXT*>( aItem );
1835 
1836  c = std::make_shared<SCH_CONNECTION>( aItem, aSheet );
1837  c->SetGraph( this );
1838  c->ConfigureFromLabel( EscapeString( text->GetShownText(), CTX_NETNAME ) );
1839  break;
1840  }
1841 
1842  default:
1843  break;
1844  }
1845 
1846  return c;
1847 }
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:50
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193

References CTX_NETNAME, EscapeString(), SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T, SCH_LABEL_T, SCH_PIN_T, and EDA_ITEM::Type().

Referenced by buildConnectionGraph(), and propagateToNeighbors().

◆ GetNetMap()

const NET_MAP& CONNECTION_GRAPH::GetNetMap ( ) const
inline

◆ GetSubgraphForItem()

CONNECTION_SUBGRAPH * CONNECTION_GRAPH::GetSubgraphForItem ( SCH_ITEM aItem)

Definition at line 2005 of file connection_graph.cpp.

2006 {
2007  if( !m_item_to_subgraph_map.count( aItem ) )
2008  return nullptr;
2009 
2010  CONNECTION_SUBGRAPH* ret = m_item_to_subgraph_map.at( aItem );
2011 
2012  while( ret->m_absorbed )
2013  ret = ret->m_absorbed_by;
2014 
2015  return ret;
2016 }
bool m_absorbed
True if this subgraph has been absorbed into another. No pointers here are safe if so!
A subgraph is a set of items that are electrically connected on a single sheet.
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
std::map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map

References CONNECTION_SUBGRAPH::m_absorbed, CONNECTION_SUBGRAPH::m_absorbed_by, and m_item_to_subgraph_map.

Referenced by SCH_EDITOR_CONTROL::UpdateNetHighlighting().

◆ matchBusMember()

SCH_CONNECTION * CONNECTION_GRAPH::matchBusMember ( SCH_CONNECTION aBusConnection,
SCH_CONNECTION aSearch 
)
staticprivate

Search for a matching bus member inside a bus connection.

For bus groups, this returns a bus member that matches aSearch by name. For bus vectors, this returns a bus member that matches by vector index.

Parameters
aBusConnectionis the bus connection to search
aSearchis the net connection to search for
Returns
a member of aBusConnection that matches aSearch

Definition at line 1850 of file connection_graph.cpp.

1852 {
1853  wxASSERT( aBusConnection->IsBus() );
1854 
1855  SCH_CONNECTION* match = nullptr;
1856 
1857  if( aBusConnection->Type() == CONNECTION_TYPE::BUS )
1858  {
1859  // Vector bus: compare against index, because we allow the name
1860  // to be different
1861 
1862  for( const auto& bus_member : aBusConnection->Members() )
1863  {
1864  if( bus_member->VectorIndex() == aSearch->VectorIndex() )
1865  {
1866  match = bus_member.get();
1867  break;
1868  }
1869  }
1870  }
1871  else
1872  {
1873  // Group bus
1874  for( const auto& c : aBusConnection->Members() )
1875  {
1876  // Vector inside group: compare names, because for bus groups
1877  // we expect the naming to be consistent across all usages
1878  // TODO(JE) explain this in the docs
1879  if( c->Type() == CONNECTION_TYPE::BUS )
1880  {
1881  for( const auto& bus_member : c->Members() )
1882  {
1883  if( bus_member->LocalName() == aSearch->LocalName() )
1884  {
1885  match = bus_member.get();
1886  break;
1887  }
1888  }
1889  }
1890  else if( c->LocalName() == aSearch->LocalName() )
1891  {
1892  match = c.get();
1893  break;
1894  }
1895  }
1896  }
1897 
1898  return match;
1899 }
wxString LocalName() const
long VectorIndex() const
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
std::vector< std::shared_ptr< SCH_CONNECTION > > & Members()
bool IsBus() const
CONNECTION_TYPE Type() const
This item represents a bus vector.

References BUS, SCH_CONNECTION::IsBus(), SCH_CONNECTION::LocalName(), SCH_CONNECTION::Members(), SCH_CONNECTION::Type(), and SCH_CONNECTION::VectorIndex().

Referenced by buildConnectionGraph(), and propagateToNeighbors().

◆ propagateToNeighbors()

void CONNECTION_GRAPH::propagateToNeighbors ( CONNECTION_SUBGRAPH aSubgraph)
private

Updates all neighbors of a subgraph with this one's connectivity info.

If this subgraph contains hierarchical links, this method will descent the hierarchy and propagate the connectivity across all linked sheets.

Definition at line 1538 of file connection_graph.cpp.

1539 {
1540  SCH_CONNECTION* conn = aSubgraph->m_driver_connection;
1541  std::vector<CONNECTION_SUBGRAPH*> search_list;
1542  std::unordered_set<CONNECTION_SUBGRAPH*> visited;
1543  std::vector<SCH_CONNECTION*> stale_bus_members;
1544 
1545  auto visit = [&] ( CONNECTION_SUBGRAPH* aParent ) {
1546  for( SCH_SHEET_PIN* pin : aParent->m_hier_pins )
1547  {
1548  SCH_SHEET_PATH path = aParent->m_sheet;
1549  path.push_back( pin->GetParent() );
1550 
1551  if( !m_sheet_to_subgraphs_map.count( path ) )
1552  continue;
1553 
1554  for( auto candidate : m_sheet_to_subgraphs_map.at( path ) )
1555  {
1556  if( !candidate->m_strong_driver ||
1557  candidate->m_hier_ports.empty() ||
1558  visited.count( candidate ) )
1559  continue;
1560 
1561  for( SCH_HIERLABEL* label : candidate->m_hier_ports )
1562  {
1563  if( label->GetShownText() == pin->GetShownText() )
1564  {
1565  wxLogTrace( "CONN", "%lu: found child %lu (%s)", aParent->m_code,
1566  candidate->m_code, candidate->m_driver_connection->Name() );
1567 
1568  candidate->m_hier_parent = aParent;
1569 
1570  search_list.push_back( candidate );
1571  break;
1572  }
1573  }
1574  }
1575  }
1576 
1577  for( SCH_HIERLABEL* label : aParent->m_hier_ports )
1578  {
1579  SCH_SHEET_PATH path = aParent->m_sheet;
1580  path.pop_back();
1581 
1582  if( !m_sheet_to_subgraphs_map.count( path ) )
1583  continue;
1584 
1585  for( auto candidate : m_sheet_to_subgraphs_map.at( path ) )
1586  {
1587  if( candidate->m_hier_pins.empty() ||
1588  visited.count( candidate ) ||
1589  ( candidate->m_driver_connection->Type() !=
1590  aParent->m_driver_connection->Type() ) )
1591  continue;
1592 
1593  for( SCH_SHEET_PIN* pin : candidate->m_hier_pins )
1594  {
1595  SCH_SHEET_PATH pin_path = path;
1596  pin_path.push_back( pin->GetParent() );
1597 
1598  if( pin_path != aParent->m_sheet )
1599  continue;
1600 
1601  if( label->GetShownText() == pin->GetShownText() )
1602  {
1603  wxLogTrace( "CONN", "%lu: found additional parent %lu (%s)",
1604  aParent->m_code, candidate->m_code,
1605  candidate->m_driver_connection->Name() );
1606 
1607  search_list.push_back( candidate );
1608  break;
1609  }
1610  }
1611  }
1612  }
1613  };
1614 
1615  auto propagate_bus_neighbors = [&]( CONNECTION_SUBGRAPH* aParentGraph ) {
1616  for( const auto& kv : aParentGraph->m_bus_neighbors )
1617  {
1618  for( CONNECTION_SUBGRAPH* neighbor : kv.second )
1619  {
1620  // May have been absorbed but won't have been deleted
1621  while( neighbor->m_absorbed )
1622  neighbor = neighbor->m_absorbed_by;
1623 
1624  SCH_CONNECTION* parent = aParentGraph->m_driver_connection;
1625 
1626  // Now member may be out of date, since we just cloned the
1627  // connection from higher up in the hierarchy. We need to
1628  // figure out what the actual new connection is.
1629  SCH_CONNECTION* member = matchBusMember( parent, kv.first.get() );
1630 
1631  if( !member )
1632  {
1633  // Try harder: we might match on a secondary driver
1634  for( CONNECTION_SUBGRAPH* sg : kv.second )
1635  {
1636  if( sg->m_multiple_drivers )
1637  {
1638  SCH_SHEET_PATH sheet = sg->m_sheet;
1639 
1640  for( SCH_ITEM* driver : sg->m_drivers )
1641  {
1642  auto c = getDefaultConnection( driver, sheet );
1643  member = matchBusMember( parent, c.get() );
1644 
1645  if( member )
1646  break;
1647  }
1648  }
1649 
1650  if( member )
1651  break;
1652  }
1653  }
1654 
1655  // This is bad, probably an ERC error
1656  if( !member )
1657  {
1658  wxLogTrace( "CONN", "Could not match bus member %s in %s",
1659  kv.first->Name(), parent->Name() );
1660  continue;
1661  }
1662 
1663  auto neighbor_conn = neighbor->m_driver_connection;
1664  auto neighbor_name = neighbor_conn->Name();
1665 
1666  // Matching name: no update needed
1667  if( neighbor_name == member->Name() )
1668  continue;
1669 
1670  // Safety check against infinite recursion
1671  wxASSERT( neighbor_conn->IsNet() );
1672 
1673  wxLogTrace( "CONN", "%lu (%s) connected to bus member %s (local %s)",
1674  neighbor->m_code, neighbor_name, member->Name(), member->LocalName() );
1675 
1676  // Take whichever name is higher priority
1679  {
1680  member->Clone( *neighbor_conn );
1681  stale_bus_members.push_back( member );
1682  }
1683  else
1684  {
1685  neighbor_conn->Clone( *member );
1686  neighbor->UpdateItemConnections();
1687 
1688  recacheSubgraphName( neighbor, neighbor_name );
1689 
1690  // Recurse onto this neighbor in case it needs to re-propagate
1691  neighbor->m_dirty = true;
1692  propagateToNeighbors( neighbor );
1693  }
1694  }
1695  }
1696  };
1697 
1698  // If we are a bus, we must propagate to local neighbors and then the hierarchy
1699  if( conn->IsBus() )
1700  propagate_bus_neighbors( aSubgraph );
1701 
1702  // If we don't have any hier pins (i.e. no children), nothing to do
1703  if( aSubgraph->m_hier_pins.empty() )
1704  {
1705  // If we also don't have any parents, we'll never be visited again
1706  if( aSubgraph->m_hier_ports.empty() )
1707  aSubgraph->m_dirty = false;
1708 
1709  return;
1710  }
1711 
1712  // If we do have hier ports, skip this subgraph as it will be visited by a parent
1713  // TODO(JE) this will leave the subgraph dirty if there is no matching parent subgraph,
1714  // which should be flagged as an ERC error
1715  if( !aSubgraph->m_hier_ports.empty() )
1716  return;
1717 
1718  visited.insert( aSubgraph );
1719 
1720  wxLogTrace( "CONN", "Propagating %lu (%s) to subsheets",
1721  aSubgraph->m_code, aSubgraph->m_driver_connection->Name() );
1722 
1723  visit( aSubgraph );
1724 
1725  for( unsigned i = 0; i < search_list.size(); i++ )
1726  {
1727  auto child = search_list[i];
1728 
1729  visited.insert( child );
1730 
1731  visit( child );
1732 
1733  child->m_dirty = false;
1734  }
1735 
1736  // Now, find the best driver for this chain of subgraphs
1737  CONNECTION_SUBGRAPH* driver = aSubgraph;
1740 
1741  // Check if a subsheet has a higher-priority connection to the same net
1743  {
1744  for( CONNECTION_SUBGRAPH* subgraph : visited )
1745  {
1747  CONNECTION_SUBGRAPH::GetDriverPriority( subgraph->m_driver );
1748 
1749  // Upgrade driver to be this subgraph if this subgraph has a power pin or global
1750  // Also upgrade if we found something with a shorter sheet path (higher in hierarchy)
1751  // but with an equivalent priority
1752 
1753  if( ( priority >= CONNECTION_SUBGRAPH::PRIORITY::POWER_PIN ) ||
1754  ( priority >= highest && subgraph->m_sheet.size() < aSubgraph->m_sheet.size() ) )
1755  driver = subgraph;
1756  }
1757  }
1758 
1759  if( driver != aSubgraph )
1760  {
1761  wxLogTrace( "CONN", "%lu (%s) overridden by new driver %lu (%s)",
1762  aSubgraph->m_code, aSubgraph->m_driver_connection->Name(),
1763  driver->m_code, driver->m_driver_connection->Name() );
1764  }
1765 
1766  conn = driver->m_driver_connection;
1767 
1768  for( CONNECTION_SUBGRAPH* subgraph : visited )
1769  {
1770  wxString old_name = subgraph->m_driver_connection->Name();
1771 
1772  subgraph->m_driver_connection->Clone( *conn );
1773  subgraph->UpdateItemConnections();
1774 
1775  if( old_name != conn->Name() )
1776  recacheSubgraphName( subgraph, old_name );
1777 
1778  if( conn->IsBus() )
1779  propagate_bus_neighbors( subgraph );
1780  }
1781 
1782  // Somewhere along the way, a bus member may have been upgraded to a global or power label.
1783  // Because this can happen anywhere, we need a second pass to update all instances of that bus
1784  // member to have the correct connection info
1785  if( conn->IsBus() && !stale_bus_members.empty() )
1786  {
1787  for( auto stale_member : stale_bus_members )
1788  {
1789  for( CONNECTION_SUBGRAPH* subgraph : visited )
1790  {
1791  SCH_CONNECTION* member = matchBusMember( subgraph->m_driver_connection,
1792  stale_member );
1793  wxASSERT( member );
1794 
1795  wxLogTrace( "CONN", "Updating %lu (%s) member %s to %s", subgraph->m_code,
1796  subgraph->m_driver_connection->Name(), member->LocalName(),
1797  stale_member->Name() );
1798 
1799  member->Clone( *stale_member );
1800 
1801  propagate_bus_neighbors( subgraph );
1802  }
1803  }
1804  }
1805 
1806  aSubgraph->m_dirty = false;
1807 }
SCH_SHEET_PATH m_sheet
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
void propagateToNeighbors(CONNECTION_SUBGRAPH *aSubgraph)
Updates all neighbors of a subgraph with this one's connectivity info.
SCH_SHEET * GetParent() const
Get the parent sheet object of this sheet pin.
Definition: sch_sheet.h:168
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!
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.
std::vector< SCH_SHEET_PIN * > m_hier_pins
wxString LocalName() const
#define kv
A subgraph is a set of items that are electrically connected on a single sheet.
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)
size_t size() const
Forwarded method from std::vector.
std::vector< SCH_ITEM * > m_drivers
const wxString & Name(bool aIgnoreSheet=false) const
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
SCH_SHEET_PATH.
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:484
std::vector< SCH_HIERLABEL * > m_hier_ports
CONNECTION_SUBGRAPH * m_absorbed_by
If this subgraph is absorbed, points to the absorbing (and valid) subgraph.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
std::shared_ptr< SCH_CONNECTION > getDefaultConnection(SCH_ITEM *aItem, const SCH_SHEET_PATH &aSheet)
Builds a new default connection for the given item based on its properties.
bool IsBus() const
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:187
static SCH_CONNECTION * matchBusMember(SCH_CONNECTION *aBusConnection, SCH_CONNECTION *aSearch)
Search for a matching bus member inside a bus connection.
void UpdateItemConnections()
Updates all items to match the driver connection.

References SCH_CONNECTION::Clone(), getDefaultConnection(), CONNECTION_SUBGRAPH::GetDriverPriority(), SCH_SHEET_PIN::GetParent(), SCH_TEXT::GetShownText(), SCH_CONNECTION::IsBus(), kv, SCH_CONNECTION::LocalName(), CONNECTION_SUBGRAPH::m_absorbed, CONNECTION_SUBGRAPH::m_absorbed_by, CONNECTION_SUBGRAPH::m_code, CONNECTION_SUBGRAPH::m_dirty, CONNECTION_SUBGRAPH::m_driver, CONNECTION_SUBGRAPH::m_driver_connection, CONNECTION_SUBGRAPH::m_drivers, CONNECTION_SUBGRAPH::m_hier_pins, CONNECTION_SUBGRAPH::m_hier_ports, CONNECTION_SUBGRAPH::m_multiple_drivers, CONNECTION_SUBGRAPH::m_sheet, m_sheet_to_subgraphs_map, matchBusMember(), SCH_CONNECTION::Name(), SCH_SHEET_PATH::pop_back(), CONNECTION_SUBGRAPH::POWER_PIN, SCH_SHEET_PATH::push_back(), recacheSubgraphName(), SCH_SHEET_PATH::size(), and CONNECTION_SUBGRAPH::UpdateItemConnections().

Referenced by buildConnectionGraph().

◆ recacheSubgraphName()

void CONNECTION_GRAPH::recacheSubgraphName ( CONNECTION_SUBGRAPH aSubgraph,
const wxString &  aOldName 
)
private

Definition at line 1902 of file connection_graph.cpp.

1904 {
1905  if( m_net_name_to_subgraphs_map.count( aOldName ) )
1906  {
1907  auto& vec = m_net_name_to_subgraphs_map.at( aOldName );
1908  vec.erase( std::remove( vec.begin(), vec.end(), aSubgraph ), vec.end() );
1909  }
1910 
1911  wxLogTrace( "CONN", "recacheSubgraphName: %s => %s", aOldName,
1912  aSubgraph->m_driver_connection->Name() );
1913 
1914  m_net_name_to_subgraphs_map[aSubgraph->m_driver_connection->Name()].push_back( aSubgraph );
1915 }
SCH_CONNECTION * m_driver_connection
Cache for driver connection.
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
const wxString & Name(bool aIgnoreSheet=false) const

References CONNECTION_SUBGRAPH::m_driver_connection, m_net_name_to_subgraphs_map, and SCH_CONNECTION::Name().

Referenced by propagateToNeighbors().

◆ Recalculate()

void CONNECTION_GRAPH::Recalculate ( const SCH_SHEET_LIST aSheetList,
bool  aUnconditional = false 
)

Updates the connection graph for the given list of sheets.

Parameters
aSheetListis the list of possibly modified sheets
aUnconditionalis true if an unconditional full recalculation should be done

Definition at line 377 of file connection_graph.cpp.

378 {
379  PROF_COUNTER recalc_time;
380  PROF_COUNTER update_items;
381 
382  if( aUnconditional )
383  Reset();
384 
385  for( const SCH_SHEET_PATH& sheet : aSheetList )
386  {
387  std::vector<SCH_ITEM*> items;
388 
389  for( auto item : sheet.LastScreen()->Items() )
390  {
391  if( item->IsConnectable() && ( aUnconditional || item->IsConnectivityDirty() ) )
392  items.push_back( item );
393  }
394 
395  updateItemConnectivity( sheet, items );
396 
397  // UpdateDanglingState() also adds connected items for SCH_TEXT
398  sheet.LastScreen()->TestDanglingEnds( &sheet );
399  }
400 
401  update_items.Stop();
402  wxLogTrace( "CONN_PROFILE", "UpdateItemConnectivity() %0.4f ms", update_items.msecs() );
403 
404  PROF_COUNTER build_graph;
405 
407 
408  build_graph.Stop();
409  wxLogTrace( "CONN_PROFILE", "BuildConnectionGraph() %0.4f ms", build_graph.msecs() );
410 
411  recalc_time.Stop();
412  wxLogTrace( "CONN_PROFILE", "Recalculate time %0.4f ms", recalc_time.msecs() );
413 
414 #ifndef DEBUG
415  // Pressure relief valve for release builds
416  const double max_recalc_time_msecs = 250.;
417 
419  recalc_time.msecs() > max_recalc_time_msecs )
420  {
421  m_allowRealTime = false;
422  }
423 #endif
424 }
void Stop()
save the time when this function was called, and set the counter stane to stop
Definition: profile.h:82
void buildConnectionGraph()
Generates the connection graph (after all item connectivity has been updated)
double msecs(bool aSinceLast=false)
Definition: profile.h:143
The class PROF_COUNTER is a small class to help profiling.
Definition: profile.h:44
void updateItemConnectivity(const SCH_SHEET_PATH &aSheet, const std::vector< SCH_ITEM * > &aItemList)
Updates the graphical connectivity between items (i.e.
SCH_SHEET_PATH.
static bool m_allowRealTime
bool m_realTimeConnectivity
Do real-time connectivity.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers of advanced config.

References buildConnectionGraph(), ADVANCED_CFG::GetCfg(), m_allowRealTime, ADVANCED_CFG::m_realTimeConnectivity, PROF_COUNTER::msecs(), Reset(), PROF_COUNTER::Stop(), and updateItemConnectivity().

Referenced by TEST_NETLISTS_FIXTURE::loadSchematic(), SCH_EDIT_FRAME::RecalculateConnections(), and SCH_SCREENS::UpdateSymbolLinks().

◆ Reset()

void CONNECTION_GRAPH::Reset ( )

Definition at line 353 of file connection_graph.cpp.

354 {
355  for( auto& subgraph : m_subgraphs )
356  delete subgraph;
357 
358  m_items.clear();
359  m_subgraphs.clear();
360  m_driver_subgraphs.clear();
361  m_sheet_to_subgraphs_map.clear();
362  m_invisible_power_pins.clear();
363  m_bus_alias_cache.clear();
364  m_net_name_to_code_map.clear();
365  m_bus_name_to_code_map.clear();
368  m_item_to_subgraph_map.clear();
369  m_local_label_cache.clear();
370  m_global_label_cache.clear();
371  m_last_net_code = 1;
372  m_last_bus_code = 1;
374 }
std::unordered_map< wxString, std::shared_ptr< BUS_ALIAS > > m_bus_alias_cache
std::unordered_map< SCH_SHEET_PATH, std::vector< CONNECTION_SUBGRAPH * > > m_sheet_to_subgraphs_map
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
NET_MAP m_net_code_to_subgraphs_map
std::unordered_map< wxString, std::vector< CONNECTION_SUBGRAPH * > > m_net_name_to_subgraphs_map
std::vector< CONNECTION_SUBGRAPH * > m_driver_subgraphs
std::map< wxString, std::vector< const CONNECTION_SUBGRAPH * > > m_global_label_cache
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
std::map< wxString, int > m_bus_name_to_code_map
std::map< wxString, int > m_net_name_to_code_map
std::unordered_set< SCH_ITEM * > m_items
std::map< std::pair< SCH_SHEET_PATH, wxString >, std::vector< const CONNECTION_SUBGRAPH * > > m_local_label_cache
std::map< SCH_ITEM *, CONNECTION_SUBGRAPH * > m_item_to_subgraph_map

References m_bus_alias_cache, m_bus_name_to_code_map, m_driver_subgraphs, m_global_label_cache, m_invisible_power_pins, m_item_to_subgraph_map, m_items, m_last_bus_code, m_last_net_code, m_last_subgraph_code, m_local_label_cache, m_net_code_to_subgraphs_map, m_net_name_to_code_map, m_net_name_to_subgraphs_map, m_sheet_to_subgraphs_map, and m_subgraphs.

Referenced by SCH_EDIT_FRAME::OpenProjectFiles(), Recalculate(), SCHEMATIC::Reset(), SCHEMATIC::SetRoot(), and ~CONNECTION_GRAPH().

◆ RunERC()

int CONNECTION_GRAPH::RunERC ( )

Runs electrical rule checks on the connectivity graph.

Precondition: graph is up-to-date

Returns
the number of errors found

NOTE:

We could check that labels attached to bus subgraphs follow the proper format (i.e. actually define a bus).

This check doesn't need to be here right now because labels won't actually be connected to bus wires if they aren't in the right format due to their TestDanglingEnds() implementation.

Definition at line 2019 of file connection_graph.cpp.

2020 {
2021  int error_count = 0;
2022 
2023  wxCHECK_MSG( m_schematic, true, "Null m_schematic in CONNECTION_GRAPH::ercCheckLabels" );
2024 
2025  ERC_SETTINGS& settings = m_schematic->ErcSettings();
2026 
2027  for( auto&& subgraph : m_subgraphs )
2028  {
2029  // Graph is supposed to be up-to-date before calling RunERC()
2030  wxASSERT( !subgraph->m_dirty );
2031 
2043  if( settings.IsTestEnabled( ERCE_DRIVER_CONFLICT ) && !subgraph->ResolveDrivers() )
2044  error_count++;
2045 
2046  if( settings.IsTestEnabled( ERCE_BUS_TO_NET_CONFLICT )
2047  && !ercCheckBusToNetConflicts( subgraph ) )
2048  error_count++;
2049 
2050  if( settings.IsTestEnabled( ERCE_BUS_ENTRY_CONFLICT )
2051  && !ercCheckBusToBusEntryConflicts( subgraph ) )
2052  error_count++;
2053 
2054  if( settings.IsTestEnabled( ERCE_BUS_TO_BUS_CONFLICT )
2055  && !ercCheckBusToBusConflicts( subgraph ) )
2056  error_count++;
2057 
2058  // The following checks are always performed since they don't currently
2059  // have an option exposed to the user
2060 
2061  if( !ercCheckNoConnects( subgraph ) )
2062  error_count++;
2063 
2064  if( ( settings.IsTestEnabled( ERCE_LABEL_NOT_CONNECTED )
2065  || settings.IsTestEnabled( ERCE_GLOBLABEL ) ) && !ercCheckLabels( subgraph ) )
2066  error_count++;
2067  }
2068 
2069  return error_count;
2070 }
bool ercCheckBusToBusEntryConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting bus entry to bus connections.
bool IsTestEnabled(int aErrorCode) const
Definition: erc_settings.h:114
std::vector< CONNECTION_SUBGRAPH * > m_subgraphs
SCHEMATIC * m_schematic
The schematic this graph represents.
bool ercCheckBusToNetConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting connections between net and bus labels.
bool ercCheckNoConnects(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for proper presence or absence of no-connect symbols.
Container for ERC settings.
Definition: erc_settings.h:87
ERC_SETTINGS & ErcSettings() const
Definition: schematic.cpp:145
bool ercCheckLabels(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for proper connection of labels.
bool ercCheckBusToBusConflicts(const CONNECTION_SUBGRAPH *aSubgraph)
Checks one subgraph for conflicting connections between two bus items.

References ercCheckBusToBusConflicts(), ercCheckBusToBusEntryConflicts(), ercCheckBusToNetConflicts(), ercCheckLabels(), ercCheckNoConnects(), ERCE_BUS_ENTRY_CONFLICT, ERCE_BUS_TO_BUS_CONFLICT, ERCE_BUS_TO_NET_CONFLICT, ERCE_DRIVER_CONFLICT, ERCE_GLOBLABEL, ERCE_LABEL_NOT_CONNECTED, SCHEMATIC::ErcSettings(), ERC_SETTINGS::IsTestEnabled(), m_schematic, and m_subgraphs.

Referenced by DIALOG_ERC::TestErc().

◆ SetSchematic()

void CONNECTION_GRAPH::SetSchematic ( SCHEMATIC aSchematic)
inline

Definition at line 237 of file connection_graph.h.

238  {
239  m_schematic = aSchematic;
240  }
SCHEMATIC * m_schematic
The schematic this graph represents.

References m_schematic.

◆ updateItemConnectivity()

void CONNECTION_GRAPH::updateItemConnectivity ( const SCH_SHEET_PATH aSheet,
const std::vector< SCH_ITEM * > &  aItemList 
)
private

Updates the graphical connectivity between items (i.e.

where they touch) The items passed in must be on the same sheet.

In the first phase, all items in aItemList have their connections initialized for the given sheet (since they may have connections on more than one sheet, and each needs to be calculated individually). The graphical connection points for the item are added to a map that stores (x, y) -> [list of items].

Any item that is stored in the list of items that have a connection point at a given (x, y) location will eventually be electrically connected. This means that we can't store SCH_COMPONENTs in this map – we must store a structure that links a specific pin on a component back to that component: a SCH_PIN_CONNECTION. This wrapper class is a convenience for linking a pin and component to a specific (x, y) point.

In the second phase, we iterate over each value in the map, which is a vector of items that have overlapping connection points. After some checks to ensure that the items should actually connect, the items are linked together using ConnectedItems().

As a side effect, items are loaded into m_items for BuildConnectionGraph()

Parameters
aSheetis the path to the sheet of all items in the list
aItemListis a list of items to consider

Definition at line 427 of file connection_graph.cpp.

429 {
430  std::unordered_map< wxPoint, std::vector<SCH_ITEM*> > connection_map;
431 
432  for( SCH_ITEM* item : aItemList )
433  {
434  std::vector< wxPoint > points;
435  item->GetConnectionPoints( points );
436  item->ConnectedItems( aSheet ).clear();
437 
438  if( item->Type() == SCH_SHEET_T )
439  {
440  for( SCH_SHEET_PIN* pin : static_cast<SCH_SHEET*>( item )->GetPins() )
441  {
442  if( !pin->Connection( aSheet ) )
443  pin->InitializeConnection( aSheet, this );
444 
445  pin->ConnectedItems( aSheet ).clear();
446  pin->Connection( aSheet )->Reset();
447 
448  connection_map[ pin->GetTextPos() ].push_back( pin );
449  m_items.insert( pin );
450  }
451  }
452  else if( item->Type() == SCH_COMPONENT_T )
453  {
454  SCH_COMPONENT* component = static_cast<SCH_COMPONENT*>( item );
455 
456  // TODO(JE) right now this relies on GetSchPins() returning good SCH_PIN pointers
457  // that contain good LIB_PIN pointers. Since these get invalidated whenever the
458  // library component is refreshed, the current solution as of ed025972 is to just
459  // rebuild the SCH_PIN list when the component is refreshed, and then re-run the
460  // connectivity calculations. This is slow and should be improved before release.
461  // See https://gitlab.com/kicad/code/kicad/issues/3784
462 
463  for( SCH_PIN* pin : component->GetSchPins( &aSheet ) )
464  {
465  pin->InitializeConnection( aSheet, this );
466 
467  wxPoint pos = pin->GetPosition();
468 
469  // because calling the first time is not thread-safe
470  pin->GetDefaultNetName( aSheet );
471  pin->ConnectedItems( aSheet ).clear();
472 
473  // Invisible power pins need to be post-processed later
474 
475  if( pin->IsPowerConnection() && !pin->IsVisible() )
476  m_invisible_power_pins.emplace_back( std::make_pair( aSheet, pin ) );
477 
478  connection_map[ pos ].push_back( pin );
479  m_items.insert( pin );
480  }
481  }
482  else
483  {
484  m_items.insert( item );
485  auto conn = item->InitializeConnection( aSheet, this );
486 
487  // Set bus/net property here so that the propagation code uses it
488  switch( item->Type() )
489  {
490  case SCH_LINE_T:
491  conn->SetType( item->GetLayer() == LAYER_BUS ? CONNECTION_TYPE::BUS :
493  break;
494 
495  case SCH_BUS_BUS_ENTRY_T:
496  conn->SetType( CONNECTION_TYPE::BUS );
497  // clean previous (old) links:
498  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[0] = nullptr;
499  static_cast<SCH_BUS_BUS_ENTRY*>( item )->m_connected_bus_items[1] = nullptr;
500  break;
501 
502  case SCH_PIN_T:
503  conn->SetType( CONNECTION_TYPE::NET );
504  break;
505 
507  conn->SetType( CONNECTION_TYPE::NET );
508  // clean previous (old) link:
509  static_cast<SCH_BUS_WIRE_ENTRY*>( item )->m_connected_bus_item = nullptr;
510  break;
511 
512  default:
513  break;
514  }
515 
516  for( const wxPoint& point : points )
517  connection_map[ point ].push_back( item );
518  }
519 
520  item->SetConnectivityDirty( false );
521  }
522 
523  for( const auto& it : connection_map )
524  {
525  auto connection_vec = it.second;
526 
527  for( auto primary_it = connection_vec.begin(); primary_it != connection_vec.end(); primary_it++ )
528  {
529  SCH_ITEM* connected_item = *primary_it;
530 
531  // Bus entries are special: they can have connection points in the
532  // middle of a wire segment, because the junction algo doesn't split
533  // the segment in two where you place a bus entry. This means that
534  // bus entries that don't land on the end of a line segment need to
535  // have "virtual" connection points to the segments they graphically
536  // touch.
537  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
538  {
539  // If this location only has the connection point of the bus
540  // entry itself, this means that either the bus entry is not
541  // connected to anything graphically, or that it is connected to
542  // a segment at some point other than at one of the endpoints.
543  if( connection_vec.size() == 1 )
544  {
545  SCH_SCREEN* screen = aSheet.LastScreen();
546  SCH_LINE* bus = screen->GetBus( it.first );
547 
548  if( bus )
549  {
550  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
551  bus_entry->m_connected_bus_item = bus;
552  }
553  }
554  }
555 
556  // Bus-to-bus entries are treated just like bus wires
557  else if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
558  {
559  if( connection_vec.size() < 2 )
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_BUS_ENTRY*>( connected_item );
567 
568  if( it.first == bus_entry->GetPosition() )
569  bus_entry->m_connected_bus_items[0] = bus;
570  else
571  bus_entry->m_connected_bus_items[1] = bus;
572 
573  bus_entry->ConnectedItems( aSheet ).insert( bus );
574  bus->ConnectedItems( aSheet ).insert( bus_entry );
575  }
576  }
577  }
578 
579  // Change junctions to be on bus junction layer if they are touching a bus
580  else if( connected_item->Type() == SCH_JUNCTION_T )
581  {
582  SCH_SCREEN* screen = aSheet.LastScreen();
583  SCH_LINE* bus = screen->GetBus( it.first );
584 
585  connected_item->SetLayer( bus ? LAYER_BUS_JUNCTION : LAYER_JUNCTION );
586  }
587 
588  for( auto test_it = primary_it + 1; test_it != connection_vec.end(); test_it++ )
589  {
590  auto test_item = *test_it;
591 
592  if( connected_item != test_item &&
593  connected_item->ConnectionPropagatesTo( test_item ) &&
594  test_item->ConnectionPropagatesTo( connected_item ) )
595  {
596  connected_item->ConnectedItems( aSheet ).insert( test_item );
597  test_item->ConnectedItems( aSheet ).insert( connected_item );
598  }
599 
600  // Set up the link between the bus entry net and the bus
601  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
602  {
603  if( test_item->Connection( aSheet )->IsBus() )
604  {
605  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
606  bus_entry->m_connected_bus_item = test_item;
607  }
608  }
609  }
610 
611  // If we got this far and did not find a connected bus item for a bus entry,
612  // we should do a manual scan in case there is a bus item on this connection
613  // point but we didn't pick it up earlier because there is *also* a net item here.
614  if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
615  {
616  auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
617 
618  if( !bus_entry->m_connected_bus_item )
619  {
620  auto screen = aSheet.LastScreen();
621  auto bus = screen->GetBus( it.first );
622 
623  if( bus )
624  bus_entry->m_connected_bus_item = bus;
625  }
626  }
627  }
628  }
629 }
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:176
virtual bool ConnectionPropagatesTo(const EDA_ITEM *aItem) const
Returns true if this item should propagate connection info to aItem.
Definition: sch_item.h:424
void SetLayer(SCH_LAYER_ID aLayer)
Set the layer this item is on.
Definition: sch_item.h:279
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet.h:84
This item represents a net.
std::vector< std::pair< SCH_SHEET_PATH, SCH_PIN * > > m_invisible_power_pins
SCH_SCREEN * LastScreen()
Function LastScreen.
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
Schematic symbol object.
Definition: sch_component.h:88
std::unordered_set< SCH_ITEM * > m_items
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:187
SCH_PIN_PTRS GetSchPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieves a list of the SCH_PINs for the given sheet path.
This item represents a bus vector.
KICAD_T Type() const
Function Type()
Definition: base_struct.h:193
SCH_LINE * GetBus(const wxPoint &aPosition, int aAccuracy=0, SCH_LINE_TEST_T aSearchType=ENTIRE_LENGTH_T)
Definition: sch_screen.h:457

References BUS, SCH_ITEM::ConnectedItems(), SCH_ITEM::ConnectionPropagatesTo(), SCH_SCREEN::GetBus(), SCH_COMPONENT::GetSchPins(), SCH_SHEET_PATH::LastScreen(), LAYER_BUS, LAYER_BUS_JUNCTION, LAYER_JUNCTION, m_invisible_power_pins, m_items, NET, SCH_BUS_BUS_ENTRY_T, SCH_BUS_WIRE_ENTRY_T, SCH_COMPONENT_T, SCH_JUNCTION_T, SCH_LINE_T, SCH_PIN_T, SCH_SHEET_T, SCH_ITEM::SetLayer(), and EDA_ITEM::Type().

Referenced by Recalculate().

Member Data Documentation

◆ m_allowRealTime

◆ m_bus_alias_cache

std::unordered_map< wxString, std::shared_ptr<BUS_ALIAS> > CONNECTION_GRAPH::m_bus_alias_cache
private

Definition at line 316 of file connection_graph.h.

Referenced by buildConnectionGraph(), GetBusAlias(), and Reset().

◆ m_bus_name_to_code_map

std::map<wxString, int> CONNECTION_GRAPH::m_bus_name_to_code_map
private

Definition at line 320 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_driver_subgraphs

std::vector<CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::m_driver_subgraphs
private

Definition at line 308 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_global_label_cache

std::map<wxString, std::vector<const CONNECTION_SUBGRAPH*> > CONNECTION_GRAPH::m_global_label_cache
private

Definition at line 322 of file connection_graph.h.

Referenced by buildConnectionGraph(), ercCheckNoConnects(), and Reset().

◆ m_invisible_power_pins

std::vector<std::pair<SCH_SHEET_PATH, SCH_PIN*> > CONNECTION_GRAPH::m_invisible_power_pins
private

Definition at line 314 of file connection_graph.h.

Referenced by buildConnectionGraph(), Reset(), and updateItemConnectivity().

◆ m_item_to_subgraph_map

std::map<SCH_ITEM*, CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::m_item_to_subgraph_map
private

Definition at line 330 of file connection_graph.h.

Referenced by buildConnectionGraph(), GetSubgraphForItem(), and Reset().

◆ m_items

std::unordered_set<SCH_ITEM*> CONNECTION_GRAPH::m_items
private

Definition at line 302 of file connection_graph.h.

Referenced by buildConnectionGraph(), Reset(), and updateItemConnectivity().

◆ m_last_bus_code

int CONNECTION_GRAPH::m_last_bus_code
private

Definition at line 336 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_last_net_code

int CONNECTION_GRAPH::m_last_net_code
private

Definition at line 334 of file connection_graph.h.

Referenced by assignNewNetCode(), and Reset().

◆ m_last_subgraph_code

int CONNECTION_GRAPH::m_last_subgraph_code
private

Definition at line 338 of file connection_graph.h.

Referenced by buildConnectionGraph(), and Reset().

◆ m_local_label_cache

std::map< std::pair<SCH_SHEET_PATH, wxString>, std::vector<const CONNECTION_SUBGRAPH*> > CONNECTION_GRAPH::m_local_label_cache
private

◆ m_net_code_to_subgraphs_map

NET_MAP CONNECTION_GRAPH::m_net_code_to_subgraphs_map
private

Definition at line 332 of file connection_graph.h.

Referenced by buildConnectionGraph(), GetNetMap(), and Reset().

◆ m_net_name_to_code_map

std::map<wxString, int> CONNECTION_GRAPH::m_net_name_to_code_map
private

Definition at line 318 of file connection_graph.h.

Referenced by assignNewNetCode(), and Reset().

◆ m_net_name_to_subgraphs_map

std::unordered_map<wxString, std::vector<CONNECTION_SUBGRAPH*> > CONNECTION_GRAPH::m_net_name_to_subgraphs_map
private

◆ m_schematic

SCHEMATIC* CONNECTION_GRAPH::m_schematic
private

The schematic this graph represents.

Definition at line 340 of file connection_graph.h.

Referenced by buildConnectionGraph(), ercCheckLabels(), RunERC(), and SetSchematic().

◆ m_sheet_to_subgraphs_map

std::unordered_map<SCH_SHEET_PATH, std::vector<CONNECTION_SUBGRAPH*> > CONNECTION_GRAPH::m_sheet_to_subgraphs_map
private

Definition at line 312 of file connection_graph.h.

Referenced by buildConnectionGraph(), propagateToNeighbors(), and Reset().

◆ m_subgraphs

std::vector<CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::m_subgraphs
private

Definition at line 305 of file connection_graph.h.

Referenced by buildConnectionGraph(), GetBusesNeedingMigration(), Reset(), and RunERC().


The documentation for this class was generated from the following files: