You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1644 lines
51 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 CERN
  5. * @author Jon Evans <jon@craftyjon.com>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <list>
  21. #include <thread>
  22. #include <unordered_map>
  23. #include <profile.h>
  24. #include <common.h>
  25. #include <erc.h>
  26. #include <sch_edit_frame.h>
  27. #include <sch_bus_entry.h>
  28. #include <sch_component.h>
  29. #include <sch_line.h>
  30. #include <sch_pin_connection.h>
  31. #include <sch_screen.h>
  32. #include <sch_sheet.h>
  33. #include <sch_sheet_path.h>
  34. #include <sch_text.h>
  35. #include <connection_graph.h>
  36. using std::map;
  37. using std::unordered_map;
  38. using std::unordered_set;
  39. using std::vector;
  40. bool CONNECTION_SUBGRAPH::ResolveDrivers( bool aCreateMarkers )
  41. {
  42. int highest_priority = -1;
  43. vector<SCH_ITEM*> candidates;
  44. m_driver = nullptr;
  45. for( auto item : m_drivers )
  46. {
  47. int item_priority = 0;
  48. switch( item->Type() )
  49. {
  50. case SCH_SHEET_PIN_T: item_priority = 2; break;
  51. case SCH_LABEL_T: item_priority = 3; break;
  52. case SCH_HIERARCHICAL_LABEL_T: item_priority = 4; break;
  53. case SCH_PIN_CONNECTION_T:
  54. {
  55. auto pin_connection = static_cast<SCH_PIN_CONNECTION*>( item );
  56. if( pin_connection->m_pin->IsPowerConnection() )
  57. item_priority = 5;
  58. else
  59. item_priority = 1;
  60. // Skip power flags, etc
  61. if( item_priority == 1 && !pin_connection->m_comp->IsInNetlist() )
  62. continue;
  63. break;
  64. }
  65. case SCH_GLOBAL_LABEL_T: item_priority = 6; break;
  66. default: break;
  67. }
  68. if( item_priority > highest_priority )
  69. {
  70. candidates.clear();
  71. candidates.push_back( item );
  72. highest_priority = item_priority;
  73. }
  74. else if( candidates.size() && ( item_priority == highest_priority ) )
  75. {
  76. candidates.push_back( item );
  77. }
  78. }
  79. if( candidates.size() )
  80. {
  81. if( candidates.size() > 1 )
  82. {
  83. if( highest_priority == 1 || highest_priority == 5 )
  84. {
  85. // We have multiple options and they are all component pins.
  86. std::sort( candidates.begin(), candidates.end(),
  87. [this]( SCH_ITEM* a, SCH_ITEM* b) -> bool
  88. {
  89. auto pin_a = static_cast<SCH_PIN_CONNECTION*>( a );
  90. auto pin_b = static_cast<SCH_PIN_CONNECTION*>( b );
  91. auto name_a = pin_a->GetDefaultNetName( m_sheet );
  92. auto name_b = pin_b->GetDefaultNetName( m_sheet );
  93. return name_a < name_b;
  94. } );
  95. }
  96. }
  97. m_driver = candidates[0];
  98. }
  99. // For power connections, we allow multiple drivers
  100. if( highest_priority == 5 && candidates.size() > 1 &&
  101. candidates[0]->Type() == SCH_PIN_CONNECTION_T )
  102. {
  103. auto pc = static_cast<SCH_PIN_CONNECTION*>( candidates[0] );
  104. wxASSERT( pc->m_pin->IsPowerConnection() );
  105. m_multiple_power_ports = true;
  106. }
  107. if( aCreateMarkers && !m_multiple_power_ports &&
  108. candidates.size() > 1 && highest_priority > 1 )
  109. {
  110. wxString msg;
  111. msg.Printf( _( "%s and %s are both attached to the same wires. "
  112. "%s was picked as the label to use for netlisting." ),
  113. candidates[0]->GetSelectMenuText( m_frame->GetUserUnits() ),
  114. candidates[1]->GetSelectMenuText( m_frame->GetUserUnits() ),
  115. candidates[0]->Connection( m_sheet )->Name() );
  116. wxASSERT( candidates[0] != candidates[1] );
  117. auto marker = new SCH_MARKER();
  118. marker->SetTimeStamp( GetNewTimeStamp() );
  119. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  120. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  121. marker->SetData( ERCE_DRIVER_CONFLICT,
  122. candidates[0]->GetPosition(), msg,
  123. candidates[1]->GetPosition() );
  124. m_sheet.LastScreen()->Append( marker );
  125. // If aCreateMarkers is true, then this is part of ERC check, so we
  126. // should return false even if the driver was assigned
  127. return false;
  128. }
  129. return aCreateMarkers || ( m_driver != nullptr );
  130. }
  131. wxString CONNECTION_SUBGRAPH::GetNetName()
  132. {
  133. if( !m_driver || m_dirty )
  134. return "";
  135. if( !m_driver->Connection( m_sheet ) )
  136. {
  137. #ifdef CONNECTIVITY_DEBUG
  138. wxASSERT_MSG( false, "Tried to get the net name of an item with no connection" );
  139. #endif
  140. return "";
  141. }
  142. return m_driver->Connection( m_sheet )->Name();
  143. }
  144. std::vector<SCH_ITEM*> CONNECTION_SUBGRAPH::GetBusLabels()
  145. {
  146. vector<SCH_ITEM*> labels;
  147. for( auto item : m_drivers )
  148. {
  149. switch( item->Type() )
  150. {
  151. case SCH_LABEL_T:
  152. case SCH_GLOBAL_LABEL_T:
  153. {
  154. auto label_conn = item->Connection( m_sheet );
  155. // Only consider bus vectors
  156. if( label_conn->Type() == CONNECTION_BUS )
  157. labels.push_back( item );
  158. }
  159. default: break;
  160. }
  161. }
  162. return labels;
  163. }
  164. void CONNECTION_GRAPH::Reset()
  165. {
  166. for( auto sg : m_subgraphs )
  167. delete sg;
  168. m_items.clear();
  169. m_subgraphs.clear();
  170. m_invisible_power_pins.clear();
  171. m_bus_alias_cache.clear();
  172. m_net_name_to_code_map.clear();
  173. m_bus_name_to_code_map.clear();
  174. m_subgraph_code_map.clear();
  175. m_net_code_to_subgraphs_map.clear();
  176. m_last_net_code = 1;
  177. m_last_bus_code = 1;
  178. m_last_subgraph_code = 1;
  179. }
  180. void CONNECTION_GRAPH::Recalculate( SCH_SHEET_LIST aSheetList )
  181. {
  182. #ifdef CONNECTIVITY_DEBUG
  183. PROF_COUNTER phase1;
  184. #endif
  185. Reset();
  186. for( const auto& sheet : aSheetList )
  187. {
  188. std::vector<SCH_ITEM*> items;
  189. for( auto item = sheet.LastScreen()->GetDrawItems();
  190. item; item = item->Next() )
  191. {
  192. if( item->IsConnectable() )
  193. {
  194. items.push_back( item );
  195. }
  196. }
  197. updateItemConnectivity( sheet, items );
  198. }
  199. #ifdef CONNECTIVITY_DEBUG
  200. phase1.Stop();
  201. std::cout << "UpdateItemConnectivity() " << phase1.msecs() << " ms" << std::endl;
  202. #endif
  203. // IsDanglingStateChanged() also adds connected items for things like SCH_TEXT
  204. SCH_SCREENS schematic;
  205. schematic.TestDanglingEnds();
  206. buildConnectionGraph();
  207. }
  208. void CONNECTION_GRAPH::updateItemConnectivity( SCH_SHEET_PATH aSheet,
  209. vector<SCH_ITEM*> aItemList )
  210. {
  211. unordered_map< wxPoint, vector<SCH_ITEM*> > connection_map;
  212. for( auto item : aItemList )
  213. {
  214. vector< wxPoint > points;
  215. item->GetConnectionPoints( points );
  216. item->ConnectedItems().clear();
  217. if( item->Type() == SCH_SHEET_T )
  218. {
  219. for( auto& pin : static_cast<SCH_SHEET*>( item )->GetPins() )
  220. {
  221. if( !pin.Connection( aSheet ) )
  222. {
  223. pin.InitializeConnection( aSheet );
  224. }
  225. pin.ConnectedItems().clear();
  226. pin.Connection( aSheet )->Reset();
  227. connection_map[ pin.GetTextPos() ].push_back( &pin );
  228. m_items.push_back( &pin );
  229. }
  230. }
  231. else if( item->Type() == SCH_COMPONENT_T )
  232. {
  233. auto component = static_cast<SCH_COMPONENT*>( item );
  234. component->UpdatePinConnections( aSheet );
  235. for( auto it : component->PinConnections() )
  236. {
  237. auto pin_connection = it.second;
  238. // TODO(JE) use cached location from m_Pins
  239. auto pin_pos = pin_connection->m_pin->GetPosition();
  240. auto pos = component->GetTransform().TransformCoordinate( pin_pos ) +
  241. component->GetPosition();
  242. // because calling the first time is not thread-safe
  243. pin_connection->GetDefaultNetName( aSheet );
  244. pin_connection->ConnectedItems().clear();
  245. connection_map[ pos ].push_back( pin_connection );
  246. m_items.push_back( pin_connection );
  247. }
  248. }
  249. else
  250. {
  251. m_items.push_back( item );
  252. if( !item->Connection( aSheet ) )
  253. {
  254. item->InitializeConnection( aSheet );
  255. }
  256. auto conn = item->Connection( aSheet );
  257. conn->Reset();
  258. // Set bus/net property here so that the propagation code uses it
  259. switch( item->Type() )
  260. {
  261. case SCH_LINE_T:
  262. conn->SetType( ( item->GetLayer() == LAYER_BUS ) ?
  263. CONNECTION_BUS : CONNECTION_NET );
  264. break;
  265. case SCH_BUS_BUS_ENTRY_T:
  266. conn->SetType( CONNECTION_BUS );
  267. break;
  268. case SCH_PIN_CONNECTION_T:
  269. case SCH_BUS_WIRE_ENTRY_T:
  270. conn->SetType( CONNECTION_NET );
  271. break;
  272. default:
  273. break;
  274. }
  275. for( auto point : points )
  276. {
  277. connection_map[ point ].push_back( item );
  278. }
  279. }
  280. }
  281. for( auto it : connection_map )
  282. {
  283. auto connection_vec = it.second;
  284. SCH_ITEM* junction = nullptr;
  285. for( auto connected_item : connection_vec )
  286. {
  287. // Look for junctions. For points that have a junction, we want all
  288. // items to connect to the junction but not to each other.
  289. if( connected_item->Type() == SCH_JUNCTION_T )
  290. {
  291. junction = connected_item;
  292. }
  293. // Bus entries are special: they can have connection points in the
  294. // middle of a wire segment, because the junction algo doesn't split
  295. // the segment in two where you place a bus entry. This means that
  296. // bus entries that don't land on the end of a line segment need to
  297. // have "virtual" connection points to the segments they graphically
  298. // touch.
  299. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  300. {
  301. // If this location only has the connection point of the bus
  302. // entry itself, this means that either the bus entry is not
  303. // connected to anything graphically, or that it is connected to
  304. // a segment at some point other than at one of the endpoints.
  305. if( connection_vec.size() == 1 )
  306. {
  307. auto screen = aSheet.LastScreen();
  308. auto bus = screen->GetBus( it.first );
  309. if( bus )
  310. {
  311. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  312. bus_entry->m_connected_bus_item = bus;
  313. }
  314. }
  315. }
  316. // Bus-to-bus entries are treated just like bus wires
  317. if( connected_item->Type() == SCH_BUS_BUS_ENTRY_T )
  318. {
  319. if( connection_vec.size() < 2 )
  320. {
  321. auto screen = aSheet.LastScreen();
  322. auto bus = screen->GetBus( it.first );
  323. if( bus )
  324. {
  325. auto bus_entry = static_cast<SCH_BUS_BUS_ENTRY*>( connected_item );
  326. if( it.first == bus_entry->GetPosition() )
  327. bus_entry->m_connected_bus_items[0] = bus;
  328. else
  329. bus_entry->m_connected_bus_items[1] = bus;
  330. bus_entry->ConnectedItems().insert( bus );
  331. bus->ConnectedItems().insert( bus_entry );
  332. }
  333. }
  334. }
  335. for( auto test_item : connection_vec )
  336. {
  337. if( !junction && test_item->Type() == SCH_JUNCTION_T )
  338. {
  339. junction = test_item;
  340. }
  341. if( connected_item != test_item &&
  342. connected_item != junction &&
  343. connected_item->ConnectionPropagatesTo( test_item ) &&
  344. test_item->ConnectionPropagatesTo( connected_item ) )
  345. {
  346. connected_item->ConnectedItems().insert( test_item );
  347. test_item->ConnectedItems().insert( connected_item );
  348. }
  349. if( connected_item->Type() == SCH_BUS_WIRE_ENTRY_T )
  350. {
  351. if( test_item->Connection( aSheet )->IsBus() )
  352. {
  353. auto bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( connected_item );
  354. bus_entry->m_connected_bus_item = test_item;
  355. }
  356. }
  357. }
  358. }
  359. }
  360. }
  361. // TODO(JE) This won't give the same subgraph IDs (and eventually net/graph codes)
  362. // to the same subgraph necessarily if it runs over and over again on the same
  363. // sheet. We need:
  364. //
  365. // a) a cache of net/bus codes, like used before
  366. // b) to persist the CONNECTION_GRAPH globally so the cache is persistent,
  367. // c) some way of trying to avoid changing net names. so we should keep track
  368. // of the previous driver of a net, and if it comes down to choosing between
  369. // equally-prioritized drivers, choose the one that already exists as a driver
  370. // on some portion of the items.
  371. void CONNECTION_GRAPH::buildConnectionGraph()
  372. {
  373. #ifdef CONNECTIVITY_DEBUG
  374. PROF_COUNTER phase2;
  375. #endif
  376. // Recache all bus aliases for later use
  377. SCH_SHEET_LIST all_sheets( g_RootSheet );
  378. for( unsigned i = 0; i < all_sheets.size(); i++ )
  379. {
  380. for( auto alias : all_sheets[i].LastScreen()->GetBusAliases() )
  381. {
  382. m_bus_alias_cache[ alias->GetName() ] = alias;
  383. }
  384. }
  385. // Build subgraphs from items (on a per-sheet basis)
  386. for( auto item : m_items )
  387. {
  388. for( auto it : item->m_connection_map )
  389. {
  390. const auto sheet = it.first;
  391. auto connection = it.second;
  392. if( connection->SubgraphCode() == 0 )
  393. {
  394. auto subgraph = new CONNECTION_SUBGRAPH( m_frame );
  395. subgraph->m_code = m_last_subgraph_code++;
  396. subgraph->m_sheet = sheet;
  397. subgraph->m_items.push_back( item );
  398. if( connection->IsDriver() )
  399. subgraph->m_drivers.push_back( item );
  400. if( item->Type() == SCH_NO_CONNECT_T )
  401. {
  402. subgraph->m_no_connect = item;
  403. }
  404. else if( item->Type() == SCH_PIN_CONNECTION_T )
  405. {
  406. auto pc = static_cast<SCH_PIN_CONNECTION*>( item );
  407. if( pc->m_pin->GetType() == PIN_NC )
  408. subgraph->m_no_connect = item;
  409. // Invisible power pins need to be post-processed later
  410. if( pc->m_pin->IsPowerConnection() &&
  411. !pc->m_pin->IsVisible() )
  412. {
  413. m_invisible_power_pins.push_back( pc );
  414. }
  415. }
  416. connection->SetSubgraphCode( subgraph->m_code );
  417. std::list<SCH_ITEM*> members( item->ConnectedItems().begin(),
  418. item->ConnectedItems().end() );
  419. for( auto connected_item : members )
  420. {
  421. if( !connected_item->Connection( sheet ) )
  422. connected_item->InitializeConnection( sheet );
  423. if( connected_item->Type() == SCH_NO_CONNECT_T )
  424. subgraph->m_no_connect = connected_item;
  425. auto connected_conn = connected_item->Connection( sheet );
  426. wxASSERT( connected_conn );
  427. if( connected_conn->SubgraphCode() == 0 )
  428. {
  429. connected_conn->SetSubgraphCode( subgraph->m_code );
  430. subgraph->m_items.push_back( connected_item );
  431. if( connected_conn->IsDriver() )
  432. subgraph->m_drivers.push_back( connected_item );
  433. members.insert( members.end(),
  434. connected_item->ConnectedItems().begin(),
  435. connected_item->ConnectedItems().end() );
  436. }
  437. }
  438. subgraph->m_dirty = true;
  439. m_subgraphs.push_back( subgraph );
  440. }
  441. }
  442. }
  443. /**
  444. * TODO(JE)
  445. *
  446. * It would be good if net codes were preserved as much as possible when
  447. * generating netlists, so that unnamed nets don't keep shifting around when
  448. * you regenerate.
  449. *
  450. * Right now, we are clearing out the old connections up in
  451. * UpdateItemConnectivity(), but that is useful information, so maybe we
  452. * need to just set the dirty flag or something.
  453. *
  454. * That way, ResolveDrivers() can check what the driver of the subgraph was
  455. * previously, and if it is in the situation of choosing between equal
  456. * candidates for an auto-generated net name, pick the previous one.
  457. *
  458. * N.B. the old algorithm solves this by sorting the possible net names
  459. * alphabetically, so as long as the same refdes components are involved,
  460. * the net will be the same.
  461. */
  462. // Resolve drivers for subgraphs and propagate connectivity info
  463. std::atomic<size_t> nextSubgraph( 0 );
  464. std::atomic<size_t> threadsFinished( 0 );
  465. size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
  466. for( size_t ii = 0; ii < parallelThreadCount; ++ii )
  467. {
  468. auto t = std::thread( [&]()
  469. {
  470. for( size_t subgraphId = nextSubgraph.fetch_add( 1 );
  471. subgraphId < static_cast<size_t>( m_subgraphs.size() );
  472. subgraphId = nextSubgraph.fetch_add( 1 ) )
  473. {
  474. auto subgraph = m_subgraphs[subgraphId];
  475. if( !subgraph->m_dirty )
  476. continue;
  477. if( !subgraph->ResolveDrivers() )
  478. {
  479. subgraph->m_dirty = false;
  480. }
  481. else
  482. {
  483. // Now the subgraph has only one driver
  484. auto driver = subgraph->m_driver;
  485. auto sheet = subgraph->m_sheet;
  486. auto connection = driver->Connection( sheet );
  487. // TODO(JE) This should live in SCH_CONNECTION probably
  488. switch( driver->Type() )
  489. {
  490. case SCH_LABEL_T:
  491. case SCH_GLOBAL_LABEL_T:
  492. case SCH_HIERARCHICAL_LABEL_T:
  493. case SCH_PIN_CONNECTION_T:
  494. case SCH_SHEET_PIN_T:
  495. case SCH_SHEET_T:
  496. {
  497. if( driver->Type() == SCH_PIN_CONNECTION_T )
  498. {
  499. auto pin = static_cast<SCH_PIN_CONNECTION*>( driver );
  500. // NOTE(JE) GetDefaultNetName is not thread-safe.
  501. connection->ConfigureFromLabel( pin->GetDefaultNetName( sheet ) );
  502. }
  503. else
  504. {
  505. auto text = static_cast<SCH_TEXT*>( driver );
  506. connection->ConfigureFromLabel( text->GetText() );
  507. }
  508. connection->SetDriver( driver );
  509. connection->ClearDirty();
  510. break;
  511. }
  512. default:
  513. break;
  514. }
  515. subgraph->m_dirty = false;
  516. }
  517. }
  518. threadsFinished++;
  519. } );
  520. t.detach();
  521. }
  522. while( threadsFinished < parallelThreadCount )
  523. std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
  524. // Generate net codes
  525. for( auto subgraph : m_subgraphs )
  526. {
  527. if( !subgraph->m_driver )
  528. continue;
  529. auto connection = subgraph->m_driver->Connection( subgraph->m_sheet );
  530. int code;
  531. auto name = subgraph->GetNetName();
  532. if( connection->IsBus() )
  533. {
  534. try
  535. {
  536. code = m_bus_name_to_code_map.at( name );
  537. }
  538. catch( const std::out_of_range& oor )
  539. {
  540. code = m_last_bus_code++;
  541. m_bus_name_to_code_map[ name ] = code;
  542. }
  543. connection->SetBusCode( code );
  544. }
  545. else
  546. {
  547. assignNewNetCode( *connection );
  548. }
  549. for( auto item : subgraph->m_items )
  550. {
  551. auto item_conn = item->Connection( subgraph->m_sheet );
  552. if( ( connection->IsBus() && item_conn->IsNet() ) ||
  553. ( connection->IsNet() && item_conn->IsBus() ) )
  554. {
  555. continue;
  556. }
  557. if( item != subgraph->m_driver )
  558. {
  559. item_conn->Clone( *connection );
  560. item_conn->ClearDirty();
  561. }
  562. }
  563. // Reset the flag for the next loop below
  564. subgraph->m_dirty = true;
  565. auto sheet = subgraph->m_sheet;
  566. auto candidate_subgraphs( m_subgraphs );
  567. auto connections_to_check( connection->Members() );
  568. bool contains_hier_stuff = false;
  569. for( auto item : subgraph->m_items )
  570. {
  571. if( item->Type() == SCH_HIERARCHICAL_LABEL_T ||
  572. item->Type() == SCH_SHEET_PIN_T )
  573. {
  574. contains_hier_stuff = true;
  575. break;
  576. }
  577. }
  578. // TODO(JE) maybe it will be better to form these links eventually,
  579. // but for now let's only include subgraphs that contain hierarchical
  580. // links in one direction or another
  581. if( !contains_hier_stuff )
  582. continue;
  583. // For plain nets, just link based on the driver
  584. if( !connection->IsBus() )
  585. {
  586. connections_to_check.push_back( std::make_shared<SCH_CONNECTION>( *connection ) );
  587. }
  588. for( unsigned i = 0; i < connections_to_check.size(); i++ )
  589. {
  590. auto member = connections_to_check[i];
  591. if( member->IsBus() )
  592. {
  593. connections_to_check.insert( connections_to_check.end(),
  594. member->Members().begin(),
  595. member->Members().end() );
  596. continue;
  597. }
  598. for( auto candidate : candidate_subgraphs )
  599. {
  600. if( candidate->m_sheet != sheet || !candidate->m_driver || candidate == subgraph )
  601. continue;
  602. auto candidate_connection = candidate->m_driver->Connection( sheet );
  603. if( !candidate_connection->IsNet() )
  604. continue;
  605. if( candidate_connection->Name( false ) == member->Name( false ) )
  606. subgraph->m_neighbor_map[ member ].push_back( candidate );
  607. }
  608. }
  609. }
  610. // Generate subgraphs for invisible power pins
  611. for( auto pc : m_invisible_power_pins )
  612. {
  613. if( pc->ConnectedItems().size() > 0 && !pc->m_pin->GetParent()->IsPower() )
  614. {
  615. // ERC will warn about this: user has wired up an invisible pin
  616. continue;
  617. }
  618. auto name = pc->m_pin->GetName();
  619. int code = -1;
  620. auto sheet = all_sheets[0];
  621. auto connection = pc->Connection( sheet );
  622. if( !connection )
  623. {
  624. pc->InitializeConnection( sheet );
  625. connection = pc->Connection( sheet );
  626. }
  627. else
  628. {
  629. continue;
  630. }
  631. try
  632. {
  633. code = m_net_name_to_code_map.at( name );
  634. }
  635. catch( const std::out_of_range& oor )
  636. {
  637. code = assignNewNetCode( *connection );
  638. }
  639. // Find a subgraph with the same net and just throw this pin on to it.
  640. // TODO(JE) should there be a dedicated subgraph for invisible pins?
  641. // Since this is currently done at the very end, the fact that some
  642. // subgraph will be getting random pins added shouldn't be a problem,
  643. // but this could be a gotcha if subgraph data is used after the end
  644. // of this method at some point in the future.
  645. CONNECTION_SUBGRAPH* subgraph = nullptr;
  646. try
  647. {
  648. auto subgraphs = m_net_code_to_subgraphs_map.at( code );
  649. subgraph = subgraphs[0];
  650. }
  651. catch( const std::out_of_range& oor )
  652. {
  653. }
  654. if( subgraph && subgraph->m_driver )
  655. {
  656. auto parent = subgraph->m_driver->Connection( subgraph->m_sheet );
  657. pc->Connection( sheet )->Clone( *parent );
  658. }
  659. else
  660. {
  661. subgraph = new CONNECTION_SUBGRAPH( m_frame );
  662. m_net_code_to_subgraphs_map[ code ].push_back( subgraph );
  663. subgraph->m_code = m_last_subgraph_code++;
  664. subgraph->m_sheet = sheet;
  665. subgraph->m_items.push_back( pc );
  666. subgraph->m_drivers.push_back( pc );
  667. subgraph->ResolveDrivers();
  668. connection->SetSubgraphCode( subgraph->m_code );
  669. }
  670. }
  671. // Collapse net codes between hierarchical sheets
  672. for( auto it = m_subgraphs.begin(); it < m_subgraphs.end(); it++ )
  673. {
  674. auto subgraph = *it;
  675. if( !subgraph->m_driver || !subgraph->m_dirty )
  676. continue;
  677. auto sheet = subgraph->m_sheet;
  678. auto connection = std::make_shared<SCH_CONNECTION>( *subgraph->m_driver->Connection( sheet ) );
  679. // Collapse power nets that are shorted together
  680. if( subgraph->m_multiple_power_ports )
  681. {
  682. for( auto obj : subgraph->m_drivers )
  683. {
  684. if( obj == subgraph->m_driver )
  685. continue;
  686. auto power_object = dynamic_cast<SCH_PIN_CONNECTION*>( obj );
  687. wxASSERT( power_object );
  688. auto name = power_object->GetDefaultNetName( subgraph->m_sheet );
  689. int code = -1;
  690. try
  691. {
  692. code = m_net_name_to_code_map.at( name );
  693. }
  694. catch( const std::out_of_range& oor )
  695. {
  696. continue;
  697. }
  698. for( auto subgraph_to_update : m_subgraphs )
  699. {
  700. if( !subgraph_to_update->m_driver )
  701. continue;
  702. auto subsheet = subgraph_to_update->m_sheet;
  703. auto conn = subgraph_to_update->m_driver->Connection( subsheet );
  704. if( conn->IsBus() || conn->NetCode() != code )
  705. continue;
  706. for( auto item : subgraph_to_update->m_items )
  707. {
  708. auto item_conn = item->Connection( subsheet );
  709. item_conn->Clone( *connection );
  710. }
  711. }
  712. }
  713. }
  714. /**
  715. * Is this bus in the highest level of hierarchy? That is, does it
  716. * contain no hierarchical ports to parent sheets? If so, we process it
  717. * here. If not, we continue, since the bus will be reached from one in
  718. * a higher level sheet.
  719. */
  720. bool contains_hier_labels = false;
  721. for( auto item : subgraph->m_drivers )
  722. {
  723. if( item->Type() == SCH_HIERARCHICAL_LABEL_T )
  724. {
  725. contains_hier_labels = true;
  726. break;
  727. }
  728. }
  729. if( contains_hier_labels )
  730. continue;
  731. // On the top level sheet, copy the neighbors onto the bus members
  732. // because the members won't have net codes yet. Then recurse into the
  733. // child sheets and propagate those net codes down.
  734. // TODO(JE) should we assign bus member net codes in the bus first, and
  735. // then reverse this operation so we overwrite the net codes generated
  736. // for the neighbors earlier rather than pulling them in?
  737. if( connection->IsBus() )
  738. {
  739. for( auto& kv : subgraph->m_neighbor_map )
  740. {
  741. auto member = kv.first;
  742. int candidate_net_code = 0;
  743. for( auto neighbor : kv.second )
  744. {
  745. auto neighbor_conn = neighbor->m_driver->Connection( sheet );
  746. try
  747. {
  748. int c = m_net_name_to_code_map.at( neighbor_conn->Name() );
  749. if( candidate_net_code == 0 )
  750. candidate_net_code = c;
  751. else
  752. {
  753. #ifdef CONNECTIVITY_DEBUG
  754. wxASSERT_MSG( false, "More than one net code for a neighbor!" );
  755. #endif
  756. }
  757. }
  758. catch( const std::out_of_range& oor )
  759. {
  760. #ifdef CONNECTIVITY_DEBUG
  761. wxASSERT_MSG( false, "No net code found for an existing net" );
  762. #endif
  763. }
  764. member->SetNetCode( candidate_net_code );
  765. }
  766. }
  767. // Some bus members might not have a neighbor to establish a net
  768. // code, so generate new ones as needed.
  769. for( auto& member : connection->Members() )
  770. {
  771. if( member->IsNet() && member->NetCode() == 0 )
  772. {
  773. assignNewNetCode( *member );
  774. }
  775. else if( member->IsBus() )
  776. {
  777. for( auto& sub_member : member->Members() )
  778. {
  779. if( sub_member->NetCode() == 0 )
  780. assignNewNetCode( *sub_member );
  781. }
  782. }
  783. }
  784. }
  785. /**
  786. * The general plan:
  787. *
  788. * Find subsheet subgraphs that match this one (because the driver is a
  789. * hierarchical label with the same name as a sheet pin on this one).
  790. *
  791. * Iterate over the bus members of the subsheet subgraph:
  792. *
  793. * 1) Find the matching bus member of the top level subgraph.
  794. * For bus groups this is just a name match (minus path).
  795. * For bus vectors the names *don't have to match*, just
  796. * the vector index!
  797. *
  798. * 2) Clone the connection of the top level subgraph onto all
  799. * the neighbor subgraphs.
  800. *
  801. * 3) Recurse down onto any subsheets connected to the SSSG.
  802. */
  803. for( auto item : subgraph->m_items )
  804. {
  805. if( item->Type() == SCH_SHEET_PIN_T )
  806. {
  807. auto sp = static_cast<SCH_SHEET_PIN*>( item );
  808. auto sp_name = sp->GetText();
  809. auto subsheet = sheet;
  810. subsheet.push_back( sp->GetParent() );
  811. for( auto candidate : m_subgraphs )
  812. {
  813. if( !candidate->m_dirty )
  814. continue;
  815. if( candidate->m_sheet == subsheet && candidate->m_driver )
  816. {
  817. auto driver = candidate->m_driver;
  818. if( ( driver->Type() == SCH_HIERARCHICAL_LABEL_T ) &&
  819. ( static_cast<SCH_HIERLABEL*>( driver )->GetText() == sp_name ) )
  820. {
  821. // We found a subgraph that is a subsheet child of
  822. // our top-level subgraph, so let's mark it
  823. candidate->m_dirty = false;
  824. auto type = driver->Connection( subsheet )->Type();
  825. // Directly update subsheet net connections
  826. for( auto c_item : candidate->m_items )
  827. {
  828. auto c = c_item->Connection( subsheet );
  829. wxASSERT( c );
  830. if( ( connection->IsBus() && c->IsNet() ) ||
  831. ( connection->IsNet() && c->IsBus() ) )
  832. {
  833. continue;
  834. }
  835. c->Clone( *connection );
  836. }
  837. // Now propagate to subsheet neighbors
  838. for( auto& kv : candidate->m_neighbor_map )
  839. {
  840. auto member = kv.first;
  841. std::shared_ptr<SCH_CONNECTION> top_level_conn;
  842. if( type == CONNECTION_BUS_GROUP )
  843. {
  844. // Bus group: match parent by name
  845. for( auto parent_member : connection->Members() )
  846. {
  847. if( parent_member->IsNet() &&
  848. parent_member->Name( true ) == member->Name( true ) )
  849. {
  850. top_level_conn = parent_member;
  851. }
  852. else if( parent_member->IsBus() )
  853. {
  854. for( auto& sub_member : parent_member->Members() )
  855. {
  856. if( sub_member->Name( true ) == member->Name( true ) )
  857. top_level_conn = sub_member;
  858. }
  859. }
  860. }
  861. }
  862. else if( type == CONNECTION_BUS )
  863. {
  864. // Bus vector: match parent by index
  865. for( auto parent_member : connection->Members() )
  866. {
  867. if( parent_member->VectorIndex() == member->VectorIndex() )
  868. top_level_conn = parent_member;
  869. }
  870. }
  871. else
  872. {
  873. top_level_conn = connection;
  874. }
  875. // If top_level_conn was not found, probably it's
  876. // an ERC error and will be caught by ERC
  877. if( !top_level_conn )
  878. {
  879. continue;
  880. }
  881. for( auto neighbor : kv.second )
  882. {
  883. for( auto n_item : neighbor->m_items )
  884. {
  885. auto c = n_item->Connection( subsheet );
  886. wxASSERT( c );
  887. c->Clone( *top_level_conn );
  888. }
  889. }
  890. }
  891. }
  892. }
  893. }
  894. }
  895. }
  896. subgraph->m_dirty = false;
  897. }
  898. m_net_code_to_subgraphs_map.clear();
  899. for( auto subgraph : m_subgraphs )
  900. {
  901. if( !subgraph->m_driver )
  902. continue;
  903. if( subgraph->m_dirty )
  904. {
  905. subgraph->m_dirty = false;
  906. }
  907. if( subgraph->m_driver->Connection( subgraph->m_sheet )->IsBus() )
  908. continue;
  909. int code = subgraph->m_driver->Connection( subgraph->m_sheet )->NetCode();
  910. m_net_code_to_subgraphs_map[ code ].push_back( subgraph );
  911. }
  912. #ifdef CONNECTIVITY_DEBUG
  913. phase2.Stop();
  914. std::cout << "BuildConnectionGraph() " << phase2.msecs() << " ms" << std::endl;
  915. #endif
  916. }
  917. int CONNECTION_GRAPH::assignNewNetCode( SCH_CONNECTION& aConnection )
  918. {
  919. int code;
  920. try
  921. {
  922. code = m_net_name_to_code_map.at( aConnection.Name() );
  923. }
  924. catch( const std::out_of_range& oor )
  925. {
  926. code = m_last_net_code++;
  927. m_net_name_to_code_map[ aConnection.Name() ] = code;
  928. }
  929. aConnection.SetNetCode( code );
  930. return code;
  931. }
  932. std::shared_ptr<BUS_ALIAS> CONNECTION_GRAPH::GetBusAlias( wxString aName )
  933. {
  934. try
  935. {
  936. return m_bus_alias_cache.at( aName );
  937. }
  938. catch( const std::out_of_range& oor )
  939. {
  940. return nullptr;
  941. }
  942. }
  943. std::vector<CONNECTION_SUBGRAPH*> CONNECTION_GRAPH::GetBusesNeedingMigration()
  944. {
  945. std::vector<CONNECTION_SUBGRAPH*> ret;
  946. for( auto it = m_subgraphs.begin(); it < m_subgraphs.end(); it++ )
  947. {
  948. auto subgraph = *it;
  949. // Graph is supposed to be up-to-date before calling this
  950. wxASSERT( !subgraph->m_dirty );
  951. if( !subgraph->m_driver )
  952. continue;
  953. auto sheet = subgraph->m_sheet;
  954. auto connection = subgraph->m_driver->Connection( sheet );
  955. if( !connection->IsBus() )
  956. continue;
  957. if( subgraph->GetBusLabels().size() > 1 )
  958. ret.push_back( subgraph );
  959. }
  960. return ret;
  961. }
  962. bool CONNECTION_GRAPH::UsesNewBusFeatures() const
  963. {
  964. for( auto it = m_subgraphs.begin(); it < m_subgraphs.end(); it++ )
  965. {
  966. auto subgraph = *it;
  967. if( !subgraph->m_driver )
  968. continue;
  969. auto sheet = subgraph->m_sheet;
  970. auto connection = subgraph->m_driver->Connection( sheet );
  971. if( !connection->IsBus() )
  972. continue;
  973. if( connection->Type() == CONNECTION_BUS_GROUP )
  974. return true;
  975. }
  976. return false;
  977. }
  978. int CONNECTION_GRAPH::RunERC( const ERC_SETTINGS& aSettings, bool aCreateMarkers )
  979. {
  980. int error_count = 0;
  981. for( auto it = m_subgraphs.begin(); it < m_subgraphs.end(); it++ )
  982. {
  983. auto subgraph = *it;
  984. // Graph is supposed to be up-to-date before calling RunERC()
  985. wxASSERT( !subgraph->m_dirty );
  986. /**
  987. * NOTE:
  988. *
  989. * We could check that labels attached to bus subgraphs follow the
  990. * proper format (i.e. actually define a bus).
  991. *
  992. * This check doesn't need to be here right now because labels
  993. * won't actually be connected to bus wires if they aren't in the right
  994. * format due to their TestDanglingEnds() implementation.
  995. */
  996. if( aSettings.check_bus_driver_conflicts &&
  997. !subgraph->ResolveDrivers( aCreateMarkers ) )
  998. error_count++;
  999. if( aSettings.check_bus_to_net_conflicts &&
  1000. !ercCheckBusToNetConflicts( subgraph, aCreateMarkers ) )
  1001. error_count++;
  1002. if( aSettings.check_bus_entry_conflicts &&
  1003. !ercCheckBusToBusEntryConflicts( subgraph, aCreateMarkers ) )
  1004. error_count++;
  1005. if( aSettings.check_bus_to_bus_conflicts &&
  1006. !ercCheckBusToBusConflicts( subgraph, aCreateMarkers ) )
  1007. error_count++;
  1008. // The following checks are always performed since they don't currently
  1009. // have an option exposed to the user
  1010. if( !ercCheckNoConnects( subgraph, aCreateMarkers ) )
  1011. error_count++;
  1012. if( !ercCheckLabels( subgraph, aCreateMarkers ) )
  1013. error_count++;
  1014. }
  1015. return error_count;
  1016. }
  1017. bool CONNECTION_GRAPH::ercCheckBusToNetConflicts( CONNECTION_SUBGRAPH* aSubgraph,
  1018. bool aCreateMarkers )
  1019. {
  1020. wxString msg;
  1021. auto sheet = aSubgraph->m_sheet;
  1022. auto screen = sheet.LastScreen();
  1023. SCH_ITEM* net_item = nullptr;
  1024. SCH_ITEM* bus_item = nullptr;
  1025. SCH_CONNECTION conn;
  1026. for( auto item : aSubgraph->m_items )
  1027. {
  1028. switch( item->Type() )
  1029. {
  1030. case SCH_LINE_T:
  1031. {
  1032. if( item->GetLayer() == LAYER_BUS )
  1033. bus_item = ( !bus_item ) ? item : bus_item;
  1034. else
  1035. net_item = ( !net_item ) ? item : net_item;
  1036. break;
  1037. }
  1038. case SCH_GLOBAL_LABEL_T:
  1039. case SCH_SHEET_PIN_T:
  1040. case SCH_HIERARCHICAL_LABEL_T:
  1041. {
  1042. auto text = static_cast<SCH_TEXT*>( item )->GetText();
  1043. conn.ConfigureFromLabel( text );
  1044. if( conn.IsBus() )
  1045. bus_item = ( !bus_item ) ? item : bus_item;
  1046. else
  1047. net_item = ( !net_item ) ? item : net_item;
  1048. break;
  1049. }
  1050. default:
  1051. break;
  1052. }
  1053. }
  1054. if( net_item && bus_item )
  1055. {
  1056. if( aCreateMarkers )
  1057. {
  1058. msg.Printf( _( "%s and %s are graphically connected but cannot"
  1059. " electrically connect because one is a bus and"
  1060. " the other is a net." ),
  1061. bus_item->GetSelectMenuText( m_frame->GetUserUnits() ),
  1062. net_item->GetSelectMenuText( m_frame->GetUserUnits() ) );
  1063. auto marker = new SCH_MARKER();
  1064. marker->SetTimeStamp( GetNewTimeStamp() );
  1065. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1066. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR );
  1067. marker->SetData( ERCE_BUS_TO_NET_CONFLICT,
  1068. net_item->GetPosition(), msg,
  1069. bus_item->GetPosition() );
  1070. screen->Append( marker );
  1071. }
  1072. return false;
  1073. }
  1074. return true;
  1075. }
  1076. bool CONNECTION_GRAPH::ercCheckBusToBusConflicts( CONNECTION_SUBGRAPH* aSubgraph,
  1077. bool aCreateMarkers )
  1078. {
  1079. wxString msg;
  1080. auto sheet = aSubgraph->m_sheet;
  1081. auto screen = sheet.LastScreen();
  1082. SCH_ITEM* label = nullptr;
  1083. SCH_ITEM* port = nullptr;
  1084. for( auto item : aSubgraph->m_items )
  1085. {
  1086. switch( item->Type() )
  1087. {
  1088. case SCH_TEXT_T:
  1089. case SCH_GLOBAL_LABEL_T:
  1090. {
  1091. if( !label && item->Connection( sheet )->IsBus() )
  1092. label = item;
  1093. break;
  1094. }
  1095. case SCH_SHEET_PIN_T:
  1096. case SCH_HIERARCHICAL_LABEL_T:
  1097. {
  1098. if( !port && item->Connection( sheet )->IsBus() )
  1099. port = item;
  1100. break;
  1101. }
  1102. default:
  1103. break;
  1104. }
  1105. }
  1106. if( label && port )
  1107. {
  1108. bool match = false;
  1109. for( auto member : label->Connection( sheet )->Members() )
  1110. {
  1111. for( auto test : port->Connection( sheet )->Members() )
  1112. {
  1113. if( test != member && member->Name() == test->Name() )
  1114. {
  1115. match = true;
  1116. break;
  1117. }
  1118. }
  1119. if( match )
  1120. break;
  1121. }
  1122. if( !match )
  1123. {
  1124. if( aCreateMarkers )
  1125. {
  1126. msg.Printf( _( "%s and %s are graphically connected but do "
  1127. "not share any bus members" ),
  1128. label->GetSelectMenuText( m_frame->GetUserUnits() ),
  1129. port->GetSelectMenuText( m_frame->GetUserUnits() ) );
  1130. auto marker = new SCH_MARKER();
  1131. marker->SetTimeStamp( GetNewTimeStamp() );
  1132. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1133. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_ERROR );
  1134. marker->SetData( ERCE_BUS_TO_BUS_CONFLICT,
  1135. label->GetPosition(), msg,
  1136. port->GetPosition() );
  1137. screen->Append( marker );
  1138. }
  1139. return false;
  1140. }
  1141. }
  1142. return true;
  1143. }
  1144. bool CONNECTION_GRAPH::ercCheckBusToBusEntryConflicts( CONNECTION_SUBGRAPH* aSubgraph,
  1145. bool aCreateMarkers )
  1146. {
  1147. wxString msg;
  1148. bool conflict = false;
  1149. auto sheet = aSubgraph->m_sheet;
  1150. auto screen = sheet.LastScreen();
  1151. SCH_BUS_WIRE_ENTRY* bus_entry = nullptr;
  1152. SCH_ITEM* bus_wire = nullptr;
  1153. for( auto item : aSubgraph->m_items )
  1154. {
  1155. switch( item->Type() )
  1156. {
  1157. case SCH_BUS_WIRE_ENTRY_T:
  1158. {
  1159. if( !bus_entry )
  1160. bus_entry = static_cast<SCH_BUS_WIRE_ENTRY*>( item );
  1161. break;
  1162. }
  1163. default:
  1164. break;
  1165. }
  1166. }
  1167. if( bus_entry && bus_entry->m_connected_bus_item )
  1168. {
  1169. bus_wire = bus_entry->m_connected_bus_item;
  1170. conflict = true;
  1171. auto test_name = bus_entry->Connection( sheet )->Name();
  1172. for( auto member : bus_wire->Connection( sheet )->Members() )
  1173. {
  1174. if( member->Type() == CONNECTION_BUS )
  1175. {
  1176. for( const auto& sub_member : member->Members() )
  1177. if( sub_member->Name() == test_name )
  1178. conflict = false;
  1179. }
  1180. else if( member->Name() == test_name )
  1181. {
  1182. conflict = false;
  1183. }
  1184. }
  1185. }
  1186. if( conflict )
  1187. {
  1188. if( aCreateMarkers )
  1189. {
  1190. msg.Printf( _( "%s (%s) is connected to %s (%s) but is not a member of the bus" ),
  1191. bus_entry->GetSelectMenuText( m_frame->GetUserUnits() ),
  1192. bus_entry->Connection( sheet )->Name(),
  1193. bus_wire->GetSelectMenuText( m_frame->GetUserUnits() ),
  1194. bus_wire->Connection( sheet )->Name() );
  1195. auto marker = new SCH_MARKER();
  1196. marker->SetTimeStamp( GetNewTimeStamp() );
  1197. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1198. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1199. marker->SetData( ERCE_BUS_ENTRY_CONFLICT,
  1200. bus_entry->GetPosition(), msg,
  1201. bus_entry->GetPosition() );
  1202. screen->Append( marker );
  1203. }
  1204. return false;
  1205. }
  1206. return true;
  1207. }
  1208. // TODO(JE) Check sheet pins here too?
  1209. bool CONNECTION_GRAPH::ercCheckNoConnects( CONNECTION_SUBGRAPH* aSubgraph,
  1210. bool aCreateMarkers )
  1211. {
  1212. wxString msg;
  1213. auto sheet = aSubgraph->m_sheet;
  1214. auto screen = sheet.LastScreen();
  1215. if( aSubgraph->m_no_connect != nullptr )
  1216. {
  1217. bool has_invalid_items = false;
  1218. SCH_PIN_CONNECTION* pin = nullptr;
  1219. std::vector<SCH_ITEM*> invalid_items;
  1220. // Any subgraph that contains both a pin and a no-connect should not
  1221. // contain any other connectable items.
  1222. for( auto item : aSubgraph->m_items )
  1223. {
  1224. switch( item->Type() )
  1225. {
  1226. case SCH_PIN_CONNECTION_T:
  1227. pin = static_cast<SCH_PIN_CONNECTION*>( item );
  1228. break;
  1229. case SCH_NO_CONNECT_T:
  1230. break;
  1231. default:
  1232. has_invalid_items = true;
  1233. invalid_items.push_back( item );
  1234. }
  1235. }
  1236. // TODO: Should it be an error to have a NC item but no pin?
  1237. if( pin && has_invalid_items )
  1238. {
  1239. auto pos = pin->GetPosition();
  1240. msg.Printf( _( "Pin %s of component %s has a no-connect marker but is connected" ),
  1241. GetChars( pin->m_pin->GetName() ),
  1242. GetChars( pin->m_comp->GetRef( &aSubgraph->m_sheet ) ) );
  1243. auto marker = new SCH_MARKER();
  1244. marker->SetTimeStamp( GetNewTimeStamp() );
  1245. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1246. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1247. marker->SetData( ERCE_NOCONNECT_CONNECTED, pos, msg, pos );
  1248. screen->Append( marker );
  1249. return false;
  1250. }
  1251. }
  1252. else
  1253. {
  1254. bool has_other_connections = false;
  1255. SCH_PIN_CONNECTION* pin = nullptr;
  1256. // Any subgraph that lacks a no-connect and contains a pin should also
  1257. // contain at least one other connectable item.
  1258. for( auto item : aSubgraph->m_items )
  1259. {
  1260. switch( item->Type() )
  1261. {
  1262. case SCH_PIN_CONNECTION_T:
  1263. if( !pin )
  1264. pin = static_cast<SCH_PIN_CONNECTION*>( item );
  1265. else
  1266. has_other_connections = true;
  1267. break;
  1268. default:
  1269. if( item->IsConnectable() )
  1270. has_other_connections = true;
  1271. break;
  1272. }
  1273. }
  1274. if( pin && !has_other_connections &&
  1275. pin->m_pin->GetType() != PIN_NC )
  1276. {
  1277. auto pos = pin->GetPosition();
  1278. msg.Printf( _( "Pin %s of component %s is unconnected." ),
  1279. GetChars( pin->m_pin->GetName() ),
  1280. GetChars( pin->m_comp->GetRef( &aSubgraph->m_sheet ) ) );
  1281. auto marker = new SCH_MARKER();
  1282. marker->SetTimeStamp( GetNewTimeStamp() );
  1283. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1284. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1285. marker->SetData( ERCE_PIN_NOT_CONNECTED, pos, msg, pos );
  1286. screen->Append( marker );
  1287. return false;
  1288. }
  1289. }
  1290. return true;
  1291. }
  1292. bool CONNECTION_GRAPH::ercCheckLabels( CONNECTION_SUBGRAPH* aSubgraph,
  1293. bool aCreateMarkers )
  1294. {
  1295. wxString msg;
  1296. auto sheet = aSubgraph->m_sheet;
  1297. auto screen = sheet.LastScreen();
  1298. SCH_TEXT* text = nullptr;
  1299. bool has_other_connections = false;
  1300. // Any subgraph that contains a label should also contain at least one other
  1301. // connectable item.
  1302. for( auto item : aSubgraph->m_items )
  1303. {
  1304. switch( item->Type() )
  1305. {
  1306. case SCH_LABEL_T:
  1307. case SCH_GLOBAL_LABEL_T:
  1308. case SCH_HIERARCHICAL_LABEL_T:
  1309. text = static_cast<SCH_TEXT*>( item );
  1310. break;
  1311. default:
  1312. if( item->IsConnectable() )
  1313. has_other_connections = true;
  1314. break;
  1315. }
  1316. }
  1317. if( text && !has_other_connections )
  1318. {
  1319. auto pos = text->GetPosition();
  1320. msg.Printf( _( "Label %s is unconnected." ),
  1321. GetChars( text->ShortenedShownText() ) );
  1322. auto marker = new SCH_MARKER();
  1323. marker->SetTimeStamp( GetNewTimeStamp() );
  1324. marker->SetMarkerType( MARKER_BASE::MARKER_ERC );
  1325. marker->SetErrorLevel( MARKER_BASE::MARKER_SEVERITY_WARNING );
  1326. marker->SetData( ERCE_LABEL_NOT_CONNECTED, pos, msg, pos );
  1327. screen->Append( marker );
  1328. return false;
  1329. }
  1330. return true;
  1331. }