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.

824 lines
25 KiB

  1. /**
  2. * @file board_netlist_updater.h
  3. * @brief BOARD_NETLIST_UPDATER class definition
  4. */
  5. /*
  6. * This program source code file is part of KiCad, a free EDA CAD application.
  7. *
  8. * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  9. * Copyright (C) 2015 CERN
  10. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  11. * Copyright (C) 2011 Wayne Stambaugh <stambaughw@verizon.net>
  12. *
  13. * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
  14. *
  15. * This program is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU General Public License
  17. * as published by the Free Software Foundation; either version 2
  18. * of the License, or (at your option) any later version.
  19. *
  20. * This program is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program; if not, you may find one here:
  27. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  28. * or you may search the http://www.gnu.org website for the version 2 license,
  29. * or you may write to the Free Software Foundation, Inc.,
  30. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  31. */
  32. #include <common.h> // for PAGE_INFO
  33. #include <class_board.h>
  34. #include <netinfo.h>
  35. #include <class_module.h>
  36. #include <class_pad.h>
  37. #include <class_zone.h>
  38. #include <pcb_netlist.h>
  39. #include <connectivity/connectivity_data.h>
  40. #include <reporter.h>
  41. #include <board_netlist_updater.h>
  42. #include <pcb_edit_frame.h>
  43. BOARD_NETLIST_UPDATER::BOARD_NETLIST_UPDATER( PCB_EDIT_FRAME* aFrame, BOARD* aBoard ) :
  44. m_frame( aFrame ),
  45. m_commit( aFrame ),
  46. m_board( aBoard )
  47. {
  48. m_reporter = &NULL_REPORTER::GetInstance();
  49. m_deleteSinglePadNets = true;
  50. m_deleteUnusedComponents = false;
  51. m_isDryRun = false;
  52. m_replaceFootprints = true;
  53. m_lookupByTimestamp = false;
  54. m_warningCount = 0;
  55. m_errorCount = 0;
  56. m_newFootprintsCount = 0;
  57. }
  58. BOARD_NETLIST_UPDATER::~BOARD_NETLIST_UPDATER()
  59. {
  60. }
  61. // These functions allow inspection of pad nets during dry runs by keeping a cache of
  62. // current pad netnames indexed by pad.
  63. void BOARD_NETLIST_UPDATER::cacheNetname( D_PAD* aPad, const wxString& aNetname )
  64. {
  65. m_padNets[ aPad ] = aNetname;
  66. }
  67. wxString BOARD_NETLIST_UPDATER::getNetname( D_PAD* aPad )
  68. {
  69. if( m_isDryRun && m_padNets.count( aPad ) )
  70. return m_padNets[ aPad ];
  71. else
  72. return aPad->GetNetname();
  73. }
  74. wxPoint BOARD_NETLIST_UPDATER::estimateComponentInsertionPosition()
  75. {
  76. wxPoint bestPosition;
  77. if( !m_board->IsEmpty() )
  78. {
  79. // Position new components below any existing board features.
  80. EDA_RECT bbox = m_board->GetBoardEdgesBoundingBox();
  81. if( bbox.GetWidth() || bbox.GetHeight() )
  82. {
  83. bestPosition.x = bbox.Centre().x;
  84. bestPosition.y = bbox.GetBottom() + Millimeter2iu( 10 );
  85. }
  86. }
  87. else
  88. {
  89. // Position new components in the center of the page when the board is empty.
  90. wxSize pageSize = m_board->GetPageSettings().GetSizeIU();
  91. bestPosition.x = pageSize.GetWidth() / 2;
  92. bestPosition.y = pageSize.GetHeight() / 2;
  93. }
  94. return bestPosition;
  95. }
  96. MODULE* BOARD_NETLIST_UPDATER::addNewComponent( COMPONENT* aComponent )
  97. {
  98. wxString msg;
  99. if( aComponent->GetFPID().empty() )
  100. {
  101. msg.Printf( _( "Cannot add %s (no footprint assigned)." ),
  102. aComponent->GetReference(),
  103. aComponent->GetFPID().Format().wx_str() );
  104. m_reporter->Report( msg, REPORTER::RPT_ERROR );
  105. ++m_errorCount;
  106. return nullptr;
  107. }
  108. MODULE* footprint = m_frame->LoadFootprint( aComponent->GetFPID() );
  109. if( footprint == nullptr )
  110. {
  111. msg.Printf( _( "Cannot add %s (footprint \"%s\" not found)." ),
  112. aComponent->GetReference(),
  113. aComponent->GetFPID().Format().wx_str() );
  114. m_reporter->Report( msg, REPORTER::RPT_ERROR );
  115. ++m_errorCount;
  116. return nullptr;
  117. }
  118. msg.Printf( _( "Add %s (footprint \"%s\")." ),
  119. aComponent->GetReference(),
  120. aComponent->GetFPID().Format().wx_str() );
  121. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  122. m_newFootprintsCount++;
  123. if( !m_isDryRun )
  124. {
  125. footprint->SetParent( m_board );
  126. footprint->SetPosition( estimateComponentInsertionPosition( ) );
  127. footprint->SetTimeStamp( GetNewTimeStamp() );
  128. m_addedComponents.push_back( footprint );
  129. m_commit.Add( footprint );
  130. return footprint;
  131. }
  132. else
  133. delete footprint;
  134. return NULL;
  135. }
  136. MODULE* BOARD_NETLIST_UPDATER::replaceComponent( NETLIST& aNetlist, MODULE* aPcbComponent,
  137. COMPONENT* aNewComponent )
  138. {
  139. wxString msg;
  140. if( aNewComponent->GetFPID().empty() )
  141. {
  142. msg.Printf( _( "Cannot update %s (no footprint assigned)." ),
  143. aNewComponent->GetReference(),
  144. aNewComponent->GetFPID().Format().wx_str() );
  145. m_reporter->Report( msg, REPORTER::RPT_ERROR );
  146. ++m_errorCount;
  147. return nullptr;
  148. }
  149. MODULE* newFootprint = m_frame->LoadFootprint( aNewComponent->GetFPID() );
  150. if( newFootprint == nullptr )
  151. {
  152. msg.Printf( _( "Cannot update %s (footprint \"%s\" not found)." ),
  153. aNewComponent->GetReference(),
  154. aNewComponent->GetFPID().Format().wx_str() );
  155. m_reporter->Report( msg, REPORTER::RPT_ERROR );
  156. ++m_errorCount;
  157. return nullptr;
  158. }
  159. msg.Printf( _( "Change %s footprint from \"%s\" to \"%s\"."),
  160. aPcbComponent->GetReference(),
  161. aPcbComponent->GetFPID().Format().wx_str(),
  162. aNewComponent->GetFPID().Format().wx_str() );
  163. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  164. m_newFootprintsCount++;
  165. if( !m_isDryRun )
  166. {
  167. m_frame->Exchange_Module( aPcbComponent, newFootprint, m_commit, true, true, true );
  168. return newFootprint;
  169. }
  170. else
  171. delete newFootprint;
  172. return nullptr;
  173. }
  174. bool BOARD_NETLIST_UPDATER::updateComponentParameters( MODULE* aPcbComponent,
  175. COMPONENT* aNewComponent )
  176. {
  177. wxString msg;
  178. // Create a copy only if the module has not been added during this update
  179. MODULE* copy = m_commit.GetStatus( aPcbComponent ) ? nullptr : (MODULE*) aPcbComponent->Clone();
  180. bool changed = false;
  181. // Test for reference designator field change.
  182. if( aPcbComponent->GetReference() != aNewComponent->GetReference() )
  183. {
  184. msg.Printf( _( "Change %s reference to %s." ),
  185. aPcbComponent->GetReference(),
  186. aNewComponent->GetReference() );
  187. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  188. if ( !m_isDryRun )
  189. {
  190. changed = true;
  191. aPcbComponent->SetReference( aNewComponent->GetReference() );
  192. }
  193. }
  194. // Test for value field change.
  195. if( aPcbComponent->GetValue() != aNewComponent->GetValue() )
  196. {
  197. msg.Printf( _( "Change %s value from %s to %s." ),
  198. aPcbComponent->GetReference(),
  199. aPcbComponent->GetValue(),
  200. aNewComponent->GetValue() );
  201. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  202. if( !m_isDryRun )
  203. {
  204. changed = true;
  205. aPcbComponent->SetValue( aNewComponent->GetValue() );
  206. }
  207. }
  208. // Test for time stamp change.
  209. if( aPcbComponent->GetPath() != aNewComponent->GetTimeStamp() )
  210. {
  211. msg.Printf( _( "Change symbol path \"%s:%s\" to \"%s\"." ),
  212. aPcbComponent->GetReference(),
  213. aPcbComponent->GetPath(),
  214. aNewComponent->GetTimeStamp() );
  215. m_reporter->Report( msg, REPORTER::RPT_INFO );
  216. if( !m_isDryRun )
  217. {
  218. changed = true;
  219. aPcbComponent->SetPath( aNewComponent->GetTimeStamp() );
  220. }
  221. }
  222. if( changed && copy )
  223. m_commit.Modified( aPcbComponent, copy );
  224. else
  225. delete copy;
  226. return true;
  227. }
  228. bool BOARD_NETLIST_UPDATER::updateComponentPadConnections( MODULE* aPcbComponent,
  229. COMPONENT* aNewComponent )
  230. {
  231. wxString msg;
  232. // Create a copy only if the module has not been added during this update
  233. MODULE* copy = m_commit.GetStatus( aPcbComponent ) ? nullptr : (MODULE*) aPcbComponent->Clone();
  234. bool changed = false;
  235. // At this point, the component footprint is updated. Now update the nets.
  236. for( auto pad : aPcbComponent->Pads() )
  237. {
  238. COMPONENT_NET net = aNewComponent->GetNet( pad->GetName() );
  239. if( !net.IsValid() ) // New footprint pad has no net.
  240. {
  241. if( !pad->GetNetname().IsEmpty() )
  242. {
  243. msg.Printf( _( "Disconnect %s pin %s." ),
  244. aPcbComponent->GetReference(),
  245. pad->GetName() );
  246. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  247. }
  248. if( !m_isDryRun )
  249. {
  250. changed = true;
  251. pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
  252. }
  253. else
  254. cacheNetname( pad, wxEmptyString );
  255. }
  256. else // New footprint pad has a net.
  257. {
  258. const wxString& netName = net.GetNetName();
  259. NETINFO_ITEM* netinfo = m_board->FindNet( netName );
  260. if( netinfo && !m_isDryRun )
  261. netinfo->SetIsCurrent( true );
  262. if( pad->GetNetname() != netName )
  263. {
  264. if( netinfo == nullptr )
  265. {
  266. // It might be a new net that has not been added to the board yet
  267. if( m_addedNets.count( netName ) )
  268. netinfo = m_addedNets[ netName ];
  269. }
  270. if( netinfo == nullptr )
  271. {
  272. netinfo = new NETINFO_ITEM( m_board, netName );
  273. // It is a new net, we have to add it
  274. if( !m_isDryRun )
  275. {
  276. changed = true;
  277. m_commit.Add( netinfo );
  278. }
  279. m_addedNets[netName] = netinfo;
  280. msg.Printf( _( "Add net %s." ), UnescapeString( netName ) );
  281. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  282. }
  283. if( !pad->GetNetname().IsEmpty() )
  284. {
  285. m_oldToNewNets[ pad->GetNetname() ] = netName;
  286. msg.Printf( _( "Reconnect %s pin %s from %s to %s."),
  287. aPcbComponent->GetReference(),
  288. pad->GetName(),
  289. UnescapeString( pad->GetNetname() ),
  290. UnescapeString( netName ) );
  291. }
  292. else
  293. {
  294. msg.Printf( _( "Connect %s pin %s to %s."),
  295. aPcbComponent->GetReference(),
  296. pad->GetName(),
  297. UnescapeString( netName ) );
  298. }
  299. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  300. if( !m_isDryRun )
  301. {
  302. changed = true;
  303. pad->SetNet( netinfo );
  304. }
  305. else
  306. cacheNetname( pad, netName );
  307. }
  308. }
  309. }
  310. if( changed && copy )
  311. m_commit.Modified( aPcbComponent, copy );
  312. else
  313. delete copy;
  314. return true;
  315. }
  316. void BOARD_NETLIST_UPDATER::cacheCopperZoneConnections()
  317. {
  318. for( int ii = 0; ii < m_board->GetAreaCount(); ii++ )
  319. {
  320. ZONE_CONTAINER* zone = m_board->GetArea( ii );
  321. if( !zone->IsOnCopperLayer() || zone->GetIsKeepout() )
  322. continue;
  323. m_zoneConnectionsCache[ zone ] = m_board->GetConnectivity()->GetConnectedPads( zone );
  324. }
  325. }
  326. bool BOARD_NETLIST_UPDATER::updateCopperZoneNets( NETLIST& aNetlist )
  327. {
  328. wxString msg;
  329. std::set<wxString> netlistNetnames;
  330. for( int ii = 0; ii < (int) aNetlist.GetCount(); ii++ )
  331. {
  332. const COMPONENT* component = aNetlist.GetComponent( ii );
  333. for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
  334. {
  335. const COMPONENT_NET& net = component->GetNet( jj );
  336. netlistNetnames.insert( net.GetNetName() );
  337. }
  338. }
  339. for( auto via : m_board->Tracks() )
  340. {
  341. if( via->Type() != PCB_VIA_T )
  342. continue;
  343. if( netlistNetnames.count( via->GetNetname() ) == 0 )
  344. {
  345. wxString updatedNetname = wxEmptyString;
  346. // Take via name from name change map if it didn't match to a new pad
  347. // (this is useful for stitching vias that don't connect to tracks)
  348. if( m_oldToNewNets.count( via->GetNetname() ) )
  349. {
  350. updatedNetname = m_oldToNewNets[via->GetNetname()];
  351. }
  352. if( !updatedNetname.IsEmpty() )
  353. {
  354. msg.Printf( _( "Reconnect via from %s to %s." ),
  355. UnescapeString( via->GetNetname() ), UnescapeString( updatedNetname ) );
  356. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  357. if( !m_isDryRun )
  358. {
  359. NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
  360. if( !netinfo )
  361. netinfo = m_addedNets[updatedNetname];
  362. if( netinfo )
  363. {
  364. m_commit.Modify( via );
  365. via->SetNet( netinfo );
  366. }
  367. }
  368. }
  369. else
  370. {
  371. msg.Printf( _( "Via connected to unknown net (%s)." ),
  372. UnescapeString( via->GetNetname() ) );
  373. m_reporter->Report( msg, REPORTER::RPT_WARNING );
  374. ++m_warningCount;
  375. }
  376. }
  377. }
  378. // Test copper zones to detect "dead" nets (nets without any pad):
  379. for( int i = 0; i < m_board->GetAreaCount(); i++ )
  380. {
  381. ZONE_CONTAINER* zone = m_board->GetArea( i );
  382. if( !zone->IsOnCopperLayer() || zone->GetIsKeepout() )
  383. continue;
  384. if( netlistNetnames.count( zone->GetNetname() ) == 0 )
  385. {
  386. // Look for a pad in the zone's connected-pad-cache which has been updated to
  387. // a new net and use that. While this won't always be the right net, the dead
  388. // net is guaranteed to be wrong.
  389. wxString updatedNetname = wxEmptyString;
  390. for( D_PAD* pad : m_zoneConnectionsCache[ zone ] )
  391. {
  392. if( getNetname( pad ) != zone->GetNetname() )
  393. {
  394. updatedNetname = getNetname( pad );
  395. break;
  396. }
  397. }
  398. // Take zone name from name change map if it didn't match to a new pad
  399. // (this is useful for zones on internal layers)
  400. if( updatedNetname.IsEmpty() && m_oldToNewNets.count( zone->GetNetname() ) )
  401. {
  402. updatedNetname = m_oldToNewNets[ zone->GetNetname() ];
  403. }
  404. if( !updatedNetname.IsEmpty() )
  405. {
  406. msg.Printf( _( "Reconnect copper zone from %s to %s." ),
  407. UnescapeString( zone->GetNetname() ),
  408. UnescapeString( updatedNetname ) );
  409. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  410. if( !m_isDryRun )
  411. {
  412. NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
  413. if( !netinfo )
  414. netinfo = m_addedNets[ updatedNetname ];
  415. if( netinfo )
  416. {
  417. m_commit.Modify( zone );
  418. zone->SetNet( netinfo );
  419. }
  420. }
  421. }
  422. else
  423. {
  424. msg.Printf( _( "Copper zone (%s) has no pads connected." ),
  425. UnescapeString( zone->GetNetname() ) );
  426. m_reporter->Report( msg, REPORTER::RPT_WARNING );
  427. ++m_warningCount;
  428. }
  429. }
  430. }
  431. return true;
  432. }
  433. bool BOARD_NETLIST_UPDATER::deleteUnusedComponents( NETLIST& aNetlist )
  434. {
  435. wxString msg;
  436. const COMPONENT* component;
  437. for( auto module : m_board->Modules() )
  438. {
  439. if( m_lookupByTimestamp )
  440. component = aNetlist.GetComponentByTimeStamp( module->GetPath() );
  441. else
  442. component = aNetlist.GetComponentByReference( module->GetReference() );
  443. if( component == NULL )
  444. {
  445. if( module->IsLocked() )
  446. {
  447. msg.Printf( _( "Cannot remove unused footprint %s (locked)." ), module->GetReference() );
  448. m_reporter->Report( msg, REPORTER::RPT_WARNING );
  449. continue;
  450. }
  451. msg.Printf( _( "Remove unused footprint %s." ), module->GetReference() );
  452. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  453. if( !m_isDryRun )
  454. m_commit.Remove( module );
  455. }
  456. }
  457. return true;
  458. }
  459. bool BOARD_NETLIST_UPDATER::deleteSinglePadNets()
  460. {
  461. int count = 0;
  462. wxString netname;
  463. wxString msg;
  464. D_PAD* previouspad = NULL;
  465. // We need the pad list for next tests.
  466. m_board->BuildListOfNets();
  467. std::vector<D_PAD*> padlist = m_board->GetPads();
  468. // Sort pads by netlist name
  469. std::sort( padlist.begin(), padlist.end(),
  470. [ this ]( D_PAD* a, D_PAD* b ) -> bool { return getNetname( a ) < getNetname( b ); } );
  471. for( D_PAD* pad : padlist )
  472. {
  473. if( getNetname( pad ).IsEmpty() )
  474. continue;
  475. if( netname != getNetname( pad ) ) // End of net
  476. {
  477. if( previouspad && count == 1 )
  478. {
  479. // First, see if we have a copper zone attached to this pad.
  480. // If so, this is not really a single pad net
  481. for( ZONE_CONTAINER* zone : m_board->Zones() )
  482. {
  483. if( !zone->IsOnCopperLayer() )
  484. continue;
  485. if( zone->GetIsKeepout() )
  486. continue;
  487. if( zone->GetNetname() == getNetname( previouspad ) )
  488. {
  489. count++;
  490. break;
  491. }
  492. }
  493. if( count == 1 ) // Really one pad, and nothing else
  494. {
  495. msg.Printf( _( "Remove single pad net %s." ),
  496. UnescapeString( getNetname( previouspad ) ) );
  497. m_reporter->Report( msg, REPORTER::RPT_ACTION );
  498. if( !m_isDryRun )
  499. previouspad->SetNetCode( NETINFO_LIST::UNCONNECTED );
  500. else
  501. cacheNetname( previouspad, wxEmptyString );
  502. }
  503. }
  504. netname = getNetname( pad );
  505. count = 1;
  506. }
  507. else
  508. {
  509. count++;
  510. }
  511. previouspad = pad;
  512. }
  513. // Examine last pad
  514. if( count == 1 )
  515. {
  516. if( !m_isDryRun )
  517. previouspad->SetNetCode( NETINFO_LIST::UNCONNECTED );
  518. else
  519. cacheNetname( previouspad, wxEmptyString );
  520. }
  521. return true;
  522. }
  523. bool BOARD_NETLIST_UPDATER::testConnectivity( NETLIST& aNetlist )
  524. {
  525. // Verify that board contains all pads in netlist: if it doesn't then footprints are
  526. // wrong or missing.
  527. // Note that we use references to find the footprints as they're already updated by this
  528. // point (whether by-reference or by-timestamp).
  529. wxString msg;
  530. wxString padname;
  531. for( int i = 0; i < (int) aNetlist.GetCount(); i++ )
  532. {
  533. const COMPONENT* component = aNetlist.GetComponent( i );
  534. MODULE* footprint = m_board->FindModuleByReference( component->GetReference() );
  535. if( footprint == NULL ) // It can be missing in partial designs
  536. continue;
  537. // Explore all pins/pads in component
  538. for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
  539. {
  540. const COMPONENT_NET& net = component->GetNet( jj );
  541. padname = net.GetPinName();
  542. if( footprint->FindPadByName( padname ) )
  543. continue; // OK, pad found
  544. // not found: bad footprint, report error
  545. msg.Printf( _( "%s pad %s not found in %s." ),
  546. component->GetReference(),
  547. padname,
  548. footprint->GetFPID().Format().wx_str() );
  549. m_reporter->Report( msg, REPORTER::RPT_ERROR );
  550. ++m_errorCount;
  551. }
  552. }
  553. return true;
  554. }
  555. bool BOARD_NETLIST_UPDATER::UpdateNetlist( NETLIST& aNetlist )
  556. {
  557. wxString msg;
  558. m_errorCount = 0;
  559. m_warningCount = 0;
  560. m_newFootprintsCount = 0;
  561. MODULE* lastPreexistingFootprint = m_board->Modules().back();
  562. cacheCopperZoneConnections();
  563. if( !m_isDryRun )
  564. {
  565. m_board->SetStatus( 0 );
  566. // Mark all nets (except <no net>) as stale; we'll update those to current that
  567. // we find in the netlist
  568. for( NETINFO_ITEM* net : m_board->GetNetInfo() )
  569. net->SetIsCurrent( net->GetNet() == 0 );
  570. }
  571. for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
  572. {
  573. COMPONENT* component = aNetlist.GetComponent( i );
  574. int matchCount = 0;
  575. MODULE* tmp;
  576. msg.Printf( _( "Processing component \"%s:%s:%s\"." ),
  577. component->GetReference(),
  578. component->GetTimeStamp(),
  579. component->GetFPID().Format().wx_str() );
  580. m_reporter->Report( msg, REPORTER::RPT_INFO );
  581. for( auto footprint : m_board->Modules() )
  582. {
  583. bool match = false;
  584. if( footprint )
  585. {
  586. if( m_lookupByTimestamp )
  587. match = footprint->GetPath() == component->GetTimeStamp();
  588. else
  589. match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
  590. }
  591. if( match )
  592. {
  593. tmp = footprint;
  594. if( m_replaceFootprints && component->GetFPID() != footprint->GetFPID() )
  595. tmp = replaceComponent( aNetlist, footprint, component );
  596. if( tmp )
  597. {
  598. updateComponentParameters( tmp, component );
  599. updateComponentPadConnections( tmp, component );
  600. }
  601. matchCount++;
  602. }
  603. if( footprint == lastPreexistingFootprint )
  604. {
  605. // No sense going through the newly-created footprints: end of loop
  606. break;
  607. }
  608. }
  609. if( matchCount == 0 )
  610. {
  611. tmp = addNewComponent( component );
  612. if( tmp )
  613. {
  614. updateComponentParameters( tmp, component );
  615. updateComponentPadConnections( tmp, component );
  616. }
  617. }
  618. else if( matchCount > 1 )
  619. {
  620. msg.Printf( _( "Multiple footprints found for \"%s\"." ),
  621. component->GetReference() );
  622. m_reporter->Report( msg, REPORTER::RPT_ERROR );
  623. }
  624. }
  625. updateCopperZoneNets( aNetlist );
  626. if( m_deleteUnusedComponents )
  627. deleteUnusedComponents( aNetlist );
  628. if( !m_isDryRun )
  629. {
  630. m_commit.Push( _( "Update netlist" ) );
  631. m_board->GetConnectivity()->Build( m_board );
  632. testConnectivity( aNetlist );
  633. // Now the connectivity data is rebuilt, we can delete single pads nets
  634. if( m_deleteSinglePadNets )
  635. deleteSinglePadNets();
  636. }
  637. else if( m_deleteSinglePadNets && !m_newFootprintsCount )
  638. // We can delete single net pads in dry run mode only if no new footprints
  639. // are added, because these new footprints are not actually added to the board
  640. // and the current pad list is wrong in this case.
  641. deleteSinglePadNets();
  642. if( m_isDryRun )
  643. {
  644. for( auto it : m_addedNets )
  645. delete it.second;
  646. m_addedNets.clear();
  647. }
  648. // Update the ratsnest
  649. m_reporter->ReportTail( wxT( "" ), REPORTER::RPT_ACTION );
  650. m_reporter->ReportTail( wxT( "" ), REPORTER::RPT_ACTION );
  651. msg.Printf( _( "Total warnings: %d, errors: %d." ), m_warningCount, m_errorCount );
  652. m_reporter->ReportTail( msg, REPORTER::RPT_ACTION );
  653. if( m_errorCount )
  654. {
  655. m_reporter->ReportTail( _( "Errors occurred during the netlist update. Unless you fix them "
  656. "your board will not be consistent with the schematics." ),
  657. REPORTER::RPT_ERROR );
  658. return false;
  659. }
  660. m_reporter->ReportTail( _( "Netlist update successful!" ), REPORTER::RPT_ACTION );
  661. return true;
  662. }
  663. bool BOARD_NETLIST_UPDATER::UpdateNetlist( const wxString& aNetlistFileName,
  664. const wxString& aCmpFileName )
  665. {
  666. return false;
  667. }