KiCad PCB EDA Suite
board_netlist_updater.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2015 CERN
6  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
7  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
8  *
9  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, you may find one here:
23  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24  * or you may search the http://www.gnu.org website for the version 2 license,
25  * or you may write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28 
29 
30 #include <common.h> // for PAGE_INFO
31 
32 #include <board.h>
33 #include <netinfo.h>
34 #include <footprint.h>
35 #include <pad.h>
36 #include <pcb_track.h>
37 #include <zone.h>
38 #include <string_utils.h>
39 #include <pcbnew_settings.h>
40 #include <pcb_edit_frame.h>
43 #include <reporter.h>
44 
45 #include "board_netlist_updater.h"
46 
47 
49  m_frame( aFrame ),
50  m_commit( aFrame ),
51  m_board( aBoard )
52 {
54 
56  m_isDryRun = false;
57  m_replaceFootprints = true;
58  m_lookupByTimestamp = false;
59 
60  m_warningCount = 0;
61  m_errorCount = 0;
63 }
64 
65 
67 {
68 }
69 
70 
71 // These functions allow inspection of pad nets during dry runs by keeping a cache of
72 // current pad netnames indexed by pad.
73 
74 void BOARD_NETLIST_UPDATER::cacheNetname( PAD* aPad, const wxString& aNetname )
75 {
76  m_padNets[ aPad ] = aNetname;
77 }
78 
79 
81 {
82  if( m_isDryRun && m_padNets.count( aPad ) )
83  return m_padNets[ aPad ];
84  else
85  return aPad->GetNetname();
86 }
87 
88 
89 void BOARD_NETLIST_UPDATER::cachePinFunction( PAD* aPad, const wxString& aPinFunction )
90 {
91  m_padPinFunctions[ aPad ] = aPinFunction;
92 }
93 
94 
96 {
97  if( m_isDryRun && m_padPinFunctions.count( aPad ) )
98  return m_padPinFunctions[ aPad ];
99  else
100  return aPad->GetPinFunction();
101 }
102 
103 
105 {
106  wxPoint bestPosition;
107 
108  if( !m_board->IsEmpty() )
109  {
110  // Position new components below any existing board features.
112 
113  if( bbox.GetWidth() || bbox.GetHeight() )
114  {
115  bestPosition.x = bbox.Centre().x;
116  bestPosition.y = bbox.GetBottom() + Millimeter2iu( 10 );
117  }
118  }
119  else
120  {
121  // Position new components in the center of the page when the board is empty.
122  wxSize pageSize = m_board->GetPageSettings().GetSizeIU();
123 
124  bestPosition.x = pageSize.GetWidth() / 2;
125  bestPosition.y = pageSize.GetHeight() / 2;
126  }
127 
128  return bestPosition;
129 }
130 
131 
133 {
134  wxString msg;
135 
136  if( aComponent->GetFPID().empty() )
137  {
138  msg.Printf( _( "Cannot add %s (no footprint assigned)." ),
139  aComponent->GetReference(),
140  aComponent->GetFPID().Format().wx_str() );
142  ++m_errorCount;
143  return nullptr;
144  }
145 
146  FOOTPRINT* footprint = m_frame->LoadFootprint( aComponent->GetFPID() );
147 
148  if( footprint == nullptr )
149  {
150  msg.Printf( _( "Cannot add %s (footprint '%s' not found)." ),
151  aComponent->GetReference(),
152  aComponent->GetFPID().Format().wx_str() );
154  ++m_errorCount;
155  return nullptr;
156  }
157 
158  if( m_isDryRun )
159  {
160  msg.Printf( _( "Add %s (footprint '%s')." ),
161  aComponent->GetReference(),
162  aComponent->GetFPID().Format().wx_str() );
163 
164  delete footprint;
165  footprint = nullptr;
166  }
167  else
168  {
169  for( PAD* pad : footprint->Pads() )
170  {
171  // Set the pads ratsnest settings to the global settings
172  pad->SetLocalRatsnestVisible( m_frame->GetDisplayOptions().m_ShowGlobalRatsnest );
173 
174  // Pads in the library all have orphaned nets. Replace with Default.
175  pad->SetNetCode( 0 );
176  }
177 
178  footprint->SetParent( m_board );
180 
181  m_addedFootprints.push_back( footprint );
182  m_commit.Add( footprint );
183 
184  msg.Printf( _( "Added %s (footprint '%s')." ),
185  aComponent->GetReference(),
186  aComponent->GetFPID().Format().wx_str() );
187  }
188 
191  return footprint;
192 }
193 
194 
196  COMPONENT* aNewComponent )
197 {
198  wxString msg;
199 
200  if( aNewComponent->GetFPID().empty() )
201  {
202  msg.Printf( _( "Cannot update %s (no footprint assigned)." ),
203  aNewComponent->GetReference(),
204  aNewComponent->GetFPID().Format().wx_str() );
206  ++m_errorCount;
207  return nullptr;
208  }
209 
210  FOOTPRINT* newFootprint = m_frame->LoadFootprint( aNewComponent->GetFPID() );
211 
212  if( newFootprint == nullptr )
213  {
214  msg.Printf( _( "Cannot update %s (footprint '%s' not found)." ),
215  aNewComponent->GetReference(),
216  aNewComponent->GetFPID().Format().wx_str() );
218  ++m_errorCount;
219  return nullptr;
220  }
221 
222  if( m_isDryRun )
223  {
224  msg.Printf( _( "Change %s footprint from '%s' to '%s'."),
225  aFootprint->GetReference(),
226  aFootprint->GetFPID().Format().wx_str(),
227  aNewComponent->GetFPID().Format().wx_str() );
228 
229  delete newFootprint;
230  newFootprint = nullptr;
231  }
232  else
233  {
234  m_frame->ExchangeFootprint( aFootprint, newFootprint, m_commit );
235 
236  msg.Printf( _( "Changed %s footprint from '%s' to '%s'."),
237  aFootprint->GetReference(),
238  aFootprint->GetFPID().Format().wx_str(),
239  aNewComponent->GetFPID().Format().wx_str() );
240  }
241 
244  return newFootprint;
245 }
246 
247 
249  COMPONENT* aNetlistComponent )
250 {
251  wxString msg;
252 
253  // Create a copy only if the footprint has not been added during this update
254  FOOTPRINT* copy = m_commit.GetStatus( aPcbFootprint ) ? nullptr
255  : (FOOTPRINT*) aPcbFootprint->Clone();
256  bool changed = false;
257 
258  // Test for reference designator field change.
259  if( aPcbFootprint->GetReference() != aNetlistComponent->GetReference() )
260  {
261  if( m_isDryRun )
262  {
263  msg.Printf( _( "Change %s reference designator to %s." ),
264  aPcbFootprint->GetReference(),
265  aNetlistComponent->GetReference() );
266  }
267  else
268  {
269  msg.Printf( _( "Changed %s reference designator to %s." ),
270  aPcbFootprint->GetReference(),
271  aNetlistComponent->GetReference() );
272 
273  changed = true;
274  aPcbFootprint->SetReference( aNetlistComponent->GetReference() );
275  }
276 
278  }
279 
280  // Test for value field change.
281  if( aPcbFootprint->GetValue() != aNetlistComponent->GetValue() )
282  {
283  if( m_isDryRun )
284  {
285  msg.Printf( _( "Change %s value from %s to %s." ),
286  aPcbFootprint->GetReference(),
287  aPcbFootprint->GetValue(),
288  aNetlistComponent->GetValue() );
289  }
290  else
291  {
292  msg.Printf( _( "Changed %s value from %s to %s." ),
293  aPcbFootprint->GetReference(),
294  aPcbFootprint->GetValue(),
295  aNetlistComponent->GetValue() );
296 
297  changed = true;
298  aPcbFootprint->SetValue( aNetlistComponent->GetValue() );
299  }
300 
302  }
303 
304  // Test for time stamp change.
305  KIID_PATH new_path = aNetlistComponent->GetPath();
306 
307  if( !aNetlistComponent->GetKIIDs().empty() )
308  new_path.push_back( aNetlistComponent->GetKIIDs().front() );
309 
310  if( aPcbFootprint->GetPath() != new_path )
311  {
312  if( m_isDryRun )
313  {
314  msg.Printf( _( "Update %s symbol association from %s to %s." ),
315  aPcbFootprint->GetReference(),
316  aPcbFootprint->GetPath().AsString(),
317  new_path.AsString() );
318  }
319  else
320  {
321  msg.Printf( _( "Updated %s symbol association from %s to %s." ),
322  aPcbFootprint->GetReference(),
323  aPcbFootprint->GetPath().AsString(),
324  new_path.AsString() );
325 
326  changed = true;
327  aPcbFootprint->SetPath( new_path );
328  }
329 
331  }
332 
333  if( aPcbFootprint->GetProperties() != aNetlistComponent->GetProperties() )
334  {
335  if( m_isDryRun )
336  {
337  msg.Printf( _( "Update %s properties." ),
338  aPcbFootprint->GetReference() );
339  }
340  else
341  {
342  msg.Printf( _( "Updated %s properties." ),
343  aPcbFootprint->GetReference() );
344 
345  changed = true;
346  aPcbFootprint->SetProperties( aNetlistComponent->GetProperties() );
347  }
348 
350  }
351 
352  if( ( aNetlistComponent->GetProperties().count( "exclude_from_bom" ) > 0 )
353  != ( ( aPcbFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 ) )
354  {
355  if( m_isDryRun )
356  {
357  if( aNetlistComponent->GetProperties().count( "exclude_from_bom" ) )
358  {
359  msg.Printf( _( "Add %s 'exclude from BOM' fabrication attribute." ),
360  aPcbFootprint->GetReference() );
361  }
362  else
363  {
364  msg.Printf( _( "Remove %s 'exclude from BOM' fabrication attribute." ),
365  aPcbFootprint->GetReference() );
366  }
367  }
368  else
369  {
370  int attributes = aPcbFootprint->GetAttributes();
371 
372  if( aNetlistComponent->GetProperties().count( "exclude_from_bom" ) )
373  {
374  attributes |= FP_EXCLUDE_FROM_BOM;
375  msg.Printf( _( "Added %s 'exclude from BOM' fabrication attribute." ),
376  aPcbFootprint->GetReference() );
377  }
378  else
379  {
380  attributes &= ~FP_EXCLUDE_FROM_BOM;
381  msg.Printf( _( "Removed %s 'exclude from BOM' fabrication attribute." ),
382  aPcbFootprint->GetReference() );
383  }
384 
385  changed = true;
386  aPcbFootprint->SetAttributes( attributes );
387  }
388 
390  }
391 
392  if( changed && copy )
393  m_commit.Modified( aPcbFootprint, copy );
394  else
395  delete copy;
396 
397  return true;
398 }
399 
400 
402  COMPONENT* aNewComponent )
403 {
404  wxString msg;
405 
406  // Create a copy only if the footprint has not been added during this update
407  FOOTPRINT* copy = m_commit.GetStatus( aFootprint ) ? nullptr : (FOOTPRINT*) aFootprint->Clone();
408  bool changed = false;
409 
410  // At this point, the component footprint is updated. Now update the nets.
411  for( PAD* pad : aFootprint->Pads() )
412  {
413  const COMPONENT_NET& net = aNewComponent->GetNet( pad->GetNumber() );
414 
415  wxString pinFunction;
416  wxString pinType;
417 
418  if( net.IsValid() ) // i.e. the pad has a name
419  {
420  pinFunction = net.GetPinFunction();
421  pinType = net.GetPinType();
422  }
423 
424  if( !m_isDryRun )
425  {
426  if( pad->GetPinFunction() != pinFunction )
427  {
428  changed = true;
429  pad->SetPinFunction( pinFunction );
430  }
431 
432  if( pad->GetPinType() != pinType )
433  {
434  changed = true;
435  pad->SetPinType( pinType );
436  }
437  }
438  else
439  {
440  cachePinFunction( pad, pinFunction );
441  }
442 
443  // Test if new footprint pad has no net (pads not on copper layers have no net).
444  if( !net.IsValid() || !pad->IsOnCopperLayer() )
445  {
446  if( !pad->GetNetname().IsEmpty() )
447  {
448  if( m_isDryRun )
449  {
450  msg.Printf( _( "Disconnect %s pin %s." ),
451  aFootprint->GetReference(),
452  pad->GetNumber() );
453  }
454  else
455  {
456  msg.Printf( _( "Disconnected %s pin %s." ),
457  aFootprint->GetReference(),
458  pad->GetNumber() );
459  }
460 
462  }
463  else if( pad->IsOnCopperLayer() && !pad->GetNumber().IsEmpty() )
464  {
465  // pad is connectable but has no net found in netlist
466  msg.Printf( _( "No net found for symbol %s pin %s." ),
467  aFootprint->GetReference(),
468  pad->GetNumber() );
470  }
471 
472  if( !m_isDryRun )
473  {
474  changed = true;
475  pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
476 
477  // If the pad has no net from netlist (i.e. not in netlist
478  // it cannot have a pin function
479  if( pad->GetNetname().IsEmpty() )
480  pad->SetPinFunction( wxEmptyString );
481 
482  }
483  else
484  {
485  cacheNetname( pad, wxEmptyString );
486  }
487  }
488  else // New footprint pad has a net.
489  {
490  const wxString& netName = net.GetNetName();
491  NETINFO_ITEM* netinfo = m_board->FindNet( netName );
492 
493  if( netinfo && !m_isDryRun )
494  netinfo->SetIsCurrent( true );
495 
496  if( pad->GetNetname() != netName )
497  {
498 
499  if( netinfo == nullptr )
500  {
501  // It might be a new net that has not been added to the board yet
502  if( m_addedNets.count( netName ) )
503  netinfo = m_addedNets[ netName ];
504  }
505 
506  if( netinfo == nullptr )
507  {
508  netinfo = new NETINFO_ITEM( m_board, netName );
509 
510  // It is a new net, we have to add it
511  if( !m_isDryRun )
512  {
513  changed = true;
514  m_commit.Add( netinfo );
515  }
516 
517  m_addedNets[netName] = netinfo;
518  msg.Printf( _( "Add net %s." ), UnescapeString( netName ) );
520  }
521 
522  if( !pad->GetNetname().IsEmpty() )
523  {
524  m_oldToNewNets[ pad->GetNetname() ] = netName;
525 
526  if( m_isDryRun )
527  {
528  msg.Printf( _( "Reconnect %s pin %s from %s to %s."),
529  aFootprint->GetReference(),
530  pad->GetNumber(),
531  UnescapeString( pad->GetNetname() ),
532  UnescapeString( netName ) );
533  }
534  else
535  {
536  msg.Printf( _( "Reconnected %s pin %s from %s to %s."),
537  aFootprint->GetReference(),
538  pad->GetNumber(),
539  UnescapeString( pad->GetNetname() ),
540  UnescapeString( netName ) );
541  }
542  }
543  else
544  {
545  if( m_isDryRun )
546  {
547  msg.Printf( _( "Connect %s pin %s to %s."),
548  aFootprint->GetReference(),
549  pad->GetNumber(),
550  UnescapeString( netName ) );
551  }
552  else
553  {
554  msg.Printf( _( "Connected %s pin %s to %s."),
555  aFootprint->GetReference(),
556  pad->GetNumber(),
557  UnescapeString( netName ) );
558  }
559  }
560 
562 
563  if( !m_isDryRun )
564  {
565  changed = true;
566  pad->SetNet( netinfo );
567  }
568  else
569  {
570  cacheNetname( pad, netName );
571  }
572  }
573  }
574  }
575 
576  if( changed && copy )
577  m_commit.Modified( aFootprint, copy );
578  else
579  delete copy;
580 
581  return true;
582 }
583 
584 
586 {
587  for( ZONE* zone : m_board->Zones() )
588  {
589  if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
590  continue;
591 
592  m_zoneConnectionsCache[ zone ] = m_board->GetConnectivity()->GetConnectedPads( zone );
593  }
594 }
595 
596 
598 {
599  wxString msg;
600  std::set<wxString> netlistNetnames;
601 
602  for( int ii = 0; ii < (int) aNetlist.GetCount(); ii++ )
603  {
604  const COMPONENT* component = aNetlist.GetComponent( ii );
605 
606  for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
607  {
608  const COMPONENT_NET& net = component->GetNet( jj );
609  netlistNetnames.insert( net.GetNetName() );
610  }
611  }
612 
613  for( PCB_TRACK* via : m_board->Tracks() )
614  {
615  if( via->Type() != PCB_VIA_T )
616  continue;
617 
618  if( netlistNetnames.count( via->GetNetname() ) == 0 )
619  {
620  wxString updatedNetname = wxEmptyString;
621 
622  // Take via name from name change map if it didn't match to a new pad
623  // (this is useful for stitching vias that don't connect to tracks)
624  if( m_oldToNewNets.count( via->GetNetname() ) )
625  {
626  updatedNetname = m_oldToNewNets[via->GetNetname()];
627  }
628 
629  if( !updatedNetname.IsEmpty() )
630  {
631  if( m_isDryRun )
632  {
633  msg.Printf( _( "Reconnect via from %s to %s." ),
634  UnescapeString( via->GetNetname() ),
635  UnescapeString( updatedNetname ) );
636 
638  }
639  else
640  {
641  NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
642 
643  if( !netinfo )
644  netinfo = m_addedNets[updatedNetname];
645 
646  if( netinfo )
647  {
648  m_commit.Modify( via );
649  via->SetNet( netinfo );
650 
651  msg.Printf( _( "Reconnected via from %s to %s." ),
652  UnescapeString( via->GetNetname() ),
653  UnescapeString( updatedNetname ) );
654 
656  }
657  }
658  }
659  else
660  {
661  msg.Printf( _( "Via connected to unknown net (%s)." ),
662  UnescapeString( via->GetNetname() ) );
664  ++m_warningCount;
665  }
666  }
667  }
668 
669  // Test copper zones to detect "dead" nets (nets without any pad):
670  for( ZONE* zone : m_board->Zones() )
671  {
672  if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
673  continue;
674 
675  if( netlistNetnames.count( zone->GetNetname() ) == 0 )
676  {
677  // Look for a pad in the zone's connected-pad-cache which has been updated to
678  // a new net and use that. While this won't always be the right net, the dead
679  // net is guaranteed to be wrong.
680  wxString updatedNetname = wxEmptyString;
681 
682  for( PAD* pad : m_zoneConnectionsCache[ zone ] )
683  {
684  if( getNetname( pad ) != zone->GetNetname() )
685  {
686  updatedNetname = getNetname( pad );
687  break;
688  }
689  }
690 
691  // Take zone name from name change map if it didn't match to a new pad
692  // (this is useful for zones on internal layers)
693  if( updatedNetname.IsEmpty() && m_oldToNewNets.count( zone->GetNetname() ) )
694  {
695  updatedNetname = m_oldToNewNets[ zone->GetNetname() ];
696  }
697 
698  if( !updatedNetname.IsEmpty() )
699  {
700  if( m_isDryRun )
701  {
702  if( !zone->GetZoneName().IsEmpty() )
703  {
704  msg.Printf( _( "Reconnect copper zone '%s' from %s to %s." ),
705  zone->GetZoneName(),
706  UnescapeString( zone->GetNetname() ),
707  UnescapeString( updatedNetname ) );
708  }
709  else
710  {
711  msg.Printf( _( "Reconnect copper zone from %s to %s." ),
712  UnescapeString( zone->GetNetname() ),
713  UnescapeString( updatedNetname ) );
714  }
715 
717  }
718  else
719  {
720  NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
721 
722  if( !netinfo )
723  netinfo = m_addedNets[ updatedNetname ];
724 
725  if( netinfo )
726  {
727  m_commit.Modify( zone );
728  zone->SetNet( netinfo );
729 
730  if( !zone->GetZoneName().IsEmpty() )
731  {
732  msg.Printf( _( "Reconnected copper zone '%s' from %s to %s." ),
733  zone->GetZoneName(),
734  UnescapeString( zone->GetNetname() ),
735  UnescapeString( updatedNetname ) );
736  }
737  else
738  {
739  msg.Printf( _( "Reconnected copper zone from %s to %s." ),
740  UnescapeString( zone->GetNetname() ),
741  UnescapeString( updatedNetname ) );
742  }
743 
745  }
746  }
747  }
748  else
749  {
750  if( !zone->GetZoneName().IsEmpty() )
751  {
752  msg.Printf( _( "Copper zone '%s' has no pads connected." ),
753  zone->GetZoneName() );
754  }
755  else
756  {
757  PCB_LAYER_ID layer = zone->GetLayer();
758  wxPoint pos = zone->GetPosition();
759 
760  msg.Printf( _( "Copper zone on layer %s at (%s, %s) has no pads connected." ),
761  m_board->GetLayerName( layer ),
764  }
765 
767  ++m_warningCount;
768  }
769  }
770  }
771 
772  return true;
773 }
774 
775 
777  std::map<COMPONENT*, FOOTPRINT*>& aFootprintMap )
778 {
779  // Verify that board contains all pads in netlist: if it doesn't then footprints are
780  // wrong or missing.
781 
782  wxString msg;
783  wxString padNumber;
784 
785  for( int i = 0; i < (int) aNetlist.GetCount(); i++ )
786  {
787  COMPONENT* component = aNetlist.GetComponent( i );
788  FOOTPRINT* footprint = aFootprintMap[component];
789 
790  if( !footprint ) // It can be missing in partial designs
791  continue;
792 
793  // Explore all pins/pads in component
794  for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
795  {
796  padNumber = component->GetNet( jj ).GetPinName();
797 
798  if( footprint->FindPadByNumber( padNumber ) )
799  continue; // OK, pad found
800 
801  // not found: bad footprint, report error
802  msg.Printf( _( "%s pad %s not found in %s." ),
803  component->GetReference(),
804  padNumber,
805  footprint->GetFPID().Format().wx_str() );
807  ++m_errorCount;
808  }
809  }
810 
811  return true;
812 }
813 
814 
816 {
817  FOOTPRINT* lastPreexistingFootprint = nullptr;
818  COMPONENT* component = nullptr;
819  wxString msg;
820 
821  m_errorCount = 0;
822  m_warningCount = 0;
824 
825  std::map<COMPONENT*, FOOTPRINT*> footprintMap;
826 
827  if( !m_board->Footprints().empty() )
828  lastPreexistingFootprint = m_board->Footprints().back();
829 
831 
832  // First mark all nets (except <no net>) as stale; we'll update those which are current
833  // in the following two loops.
834  //
835  if( !m_isDryRun )
836  {
837  m_board->SetStatus( 0 );
838 
839  for( NETINFO_ITEM* net : m_board->GetNetInfo() )
840  net->SetIsCurrent( net->GetNetCode() == 0 );
841  }
842 
843  // Next go through the netlist updating all board footprints which have matching component
844  // entries and adding new footprints for those that don't.
845  //
846  for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
847  {
848  component = aNetlist.GetComponent( i );
849 
850  if( component->GetProperties().count( "exclude_from_board" ) )
851  continue;
852 
853  msg.Printf( _( "Processing symbol '%s:%s'." ),
854  component->GetReference(),
855  component->GetFPID().Format().wx_str() );
857 
858  int matchCount = 0;
859 
860  for( FOOTPRINT* footprint : m_board->Footprints() )
861  {
862  bool match = false;
863 
864  if( m_lookupByTimestamp )
865  {
866  for( const KIID& uuid : component->GetKIIDs() )
867  {
868  KIID_PATH base = component->GetPath();
869  base.push_back( uuid );
870 
871  if( footprint->GetPath() == base )
872  {
873  match = true;
874  break;
875  }
876  }
877  }
878  else
879  {
880  match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
881  }
882 
883  if( match )
884  {
885  FOOTPRINT* tmp = footprint;
886 
887  if( m_replaceFootprints && component->GetFPID() != footprint->GetFPID() )
888  tmp = replaceFootprint( aNetlist, footprint, component );
889 
890  if( tmp )
891  {
892  footprintMap[ component ] = tmp;
893 
894  updateFootprintParameters( tmp, component );
895  updateComponentPadConnections( tmp, component );
896  }
897 
898  matchCount++;
899  }
900 
901  if( footprint == lastPreexistingFootprint )
902  {
903  // No sense going through the newly-created footprints: end of loop
904  break;
905  }
906  }
907 
908  if( matchCount == 0 )
909  {
910  FOOTPRINT* footprint = addNewFootprint( component );
911 
912  if( footprint )
913  {
914  footprintMap[ component ] = footprint;
915 
916  updateFootprintParameters( footprint, component );
917  updateComponentPadConnections( footprint, component );
918  }
919  }
920  else if( matchCount > 1 )
921  {
922  msg.Printf( _( "Multiple footprints found for '%s'." ), component->GetReference() );
924  }
925  }
926 
927  updateCopperZoneNets( aNetlist );
928 
929  // Finally go through the board footprints and update all those that *don't* have matching
930  // component entries.
931  //
932  for( FOOTPRINT* footprint : m_board->Footprints() )
933  {
934  bool matched = false;
935  bool doDelete = m_deleteUnusedFootprints;
936 
937  if( ( footprint->GetAttributes() & FP_BOARD_ONLY ) > 0 )
938  doDelete = false;
939 
940  if( m_lookupByTimestamp )
941  component = aNetlist.GetComponentByPath( footprint->GetPath() );
942  else
943  component = aNetlist.GetComponentByReference( footprint->GetReference() );
944 
945  if( component && component->GetProperties().count( "exclude_from_board" ) == 0 )
946  matched = true;
947 
948  if( doDelete && !matched && footprint->IsLocked() )
949  {
950  if( m_isDryRun )
951  {
952  msg.Printf( _( "Cannot remove unused footprint %s (locked)." ),
953  footprint->GetReference() );
954  }
955  else
956  {
957  msg.Printf( _( "Could not remove unused footprint %s (locked)." ),
958  footprint->GetReference() );
959  }
960 
962  doDelete = false;
963  }
964 
965  if( doDelete && !matched )
966  {
967  if( m_isDryRun )
968  {
969  msg.Printf( _( "Remove unused footprint %s." ), footprint->GetReference() );
970  }
971  else
972  {
973  m_commit.Remove( footprint );
974  msg.Printf( _( "Removed unused footprint %s." ), footprint->GetReference() );
975  }
976 
978  }
979  else if( !m_isDryRun )
980  {
981  if( !matched )
982  footprint->SetPath( KIID_PATH() );
983 
984  for( PAD* pad : footprint->Pads() )
985  {
986  if( pad->GetNet() )
987  pad->GetNet()->SetIsCurrent( true );
988  }
989  }
990  }
991 
992  if( !m_isDryRun )
993  {
994  m_board->GetConnectivity()->Build( m_board );
995  testConnectivity( aNetlist, footprintMap );
996 
997  for( NETINFO_ITEM* net : m_board->GetNetInfo() )
998  {
999  if( !net->IsCurrent() )
1000  {
1001  msg.Printf( _( "Removed unused net %s." ), net->GetNetname() );
1003  m_commit.Removed( net );
1004  }
1005  }
1006 
1009  m_commit.Push( _( "Update netlist" ) );
1010 
1013  }
1014 
1015  if( m_isDryRun )
1016  {
1017  for( const std::pair<const wxString, NETINFO_ITEM*>& addedNet : m_addedNets )
1018  delete addedNet.second;
1019 
1020  m_addedNets.clear();
1021  }
1022 
1023  // Update the ratsnest
1024  m_reporter->ReportTail( wxT( "" ), RPT_SEVERITY_ACTION );
1025  m_reporter->ReportTail( wxT( "" ), RPT_SEVERITY_ACTION );
1026 
1027  msg.Printf( _( "Total warnings: %d, errors: %d." ), m_warningCount, m_errorCount );
1029 
1030  return true;
1031 }
void SetReference(const wxString &aReference)
Definition: footprint.h:472
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1325
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:104
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
const PAGE_INFO & GetPageSettings() const
Definition: board.h:535
COMPONENT * GetComponentByPath(const KIID_PATH &aPath)
Return a COMPONENT by aPath.
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:362
ZONES & Zones()
Definition: board.h:239
const wxString & GetValue() const
Definition: footprint.h:485
const KIID_PATH & GetPath() const
Definition: footprint.h:203
wxString getNetname(PAD *aPad)
void SetPath(const KIID_PATH &aPath)
Definition: footprint.h:204
COMMIT & Add(EDA_ITEM *aItem)
Notify observers that aItem has been added.
Definition: commit.h:78
const EDA_RECT GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:737
void SetIsCurrent(bool isCurrent)
Definition: netinfo.h:141
const wxString & GetPinType() const
Definition: pcb_netlist.h:60
virtual REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Places the report at the end of the list, for objects that support report ordering.
Definition: reporter.h:99
int GetWidth() const
Definition: eda_rect.h:109
unsigned GetCount() const
Definition: pcb_netlist.h:228
int GetStatus(EDA_ITEM *aItem)
Definition: commit.cpp:131
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:679
void SetStatus(EDA_ITEM_FLAGS aStatus)
Definition: eda_item.h:151
std::map< wxString, wxString > m_oldToNewNets
bool updateCopperZoneNets(NETLIST &aNetlist)
void cachePinFunction(PAD *aPad, const wxString &aPinFunction)
void RemoveUnusedNets()
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
bool UpdateNetlist(NETLIST &aNetlist)
Update the board's components according to the new netlist.
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
bool empty() const
Definition: lib_id.h:180
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:116
void SetAttributes(int aAttributes)
Definition: footprint.h:236
PADS & Pads()
Definition: footprint.h:168
int GetBottom() const
Definition: eda_rect.h:114
std::map< wxString, NETINFO_ITEM * > m_addedNets
const LIB_ID & GetFPID() const
Definition: pcb_netlist.h:133
Used to store the component pin name to net name (and pin function) associations stored in a netlist.
Definition: pcb_netlist.h:43
bool updateComponentPadConnections(FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
COMMIT & Removed(EDA_ITEM *aItem)
Modify a given item in the model.
Definition: commit.h:96
void SynchronizeNetsAndNetClasses()
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:1391
wxString getPinFunction(PAD *aPad)
Definition: kiid.h:44
void ExchangeFootprint(FOOTPRINT *aExisting, FOOTPRINT *aNew, BOARD_COMMIT &aCommit, bool deleteExtraTexts=true, bool resetTextLayers=true, bool resetTextEffects=true, bool resetFabricationAttrs=true, bool reset3DModels=true)
Replace aExisting footprint by aNew footprint using the Existing footprint settings (position,...
const wxString & GetNetName() const
Definition: pcb_netlist.h:58
std::vector< FOOTPRINT * > m_addedFootprints
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
Definition: pcb_netlist.h:206
std::map< ZONE *, std::vector< PAD * > > m_zoneConnectionsCache
unsigned GetNetCount() const
Definition: pcb_netlist.h:109
FOOTPRINTS & Footprints()
Definition: board.h:233
void SaveProjectSettings() override
Save changes to the project settings to the project (.pro) file.
const wxString & GetReference() const
Definition: pcb_netlist.h:123
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:344
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
Definition: footprint.cpp:1056
const wxString & GetReference() const
Definition: footprint.h:463
#define _(s)
FOOTPRINT * replaceFootprint(NETLIST &aNetlist, FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
FOOTPRINT * LoadFootprint(const LIB_ID &aFootprintId)
Attempt to load aFootprintId from the footprint library table.
const LIB_ID & GetFPID() const
Definition: footprint.h:194
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
COMMIT & Remove(EDA_ITEM *aItem)
Notify observers that aItem has been removed.
Definition: commit.h:90
void SetValue(const wxString &aValue)
Definition: footprint.h:493
const COMPONENT_NET & GetNet(unsigned aIndex) const
Definition: pcb_netlist.h:111
int GetHeight() const
Definition: eda_rect.h:110
UTF8 Format() const
Definition: lib_id.cpp:116
wxString UnescapeString(const wxString &aSource)
Store all of the related footprint information found in a netlist.
Definition: pcb_netlist.h:84
const wxString & GetPinFunction() const
Definition: pcb_netlist.h:59
bool IsValid() const
Definition: pcb_netlist.h:62
COMPONENT * GetComponent(unsigned aIndex)
Return the COMPONENT at aIndex.
Definition: pcb_netlist.h:236
COMMIT & Modified(EDA_ITEM *aItem, EDA_ITEM *aCopy)
Definition: commit.h:110
Handle the data for a net.
Definition: netinfo.h:64
bool updateFootprintParameters(FOOTPRINT *aPcbFootprint, COMPONENT *aNetlistComponent)
int GetAttributes() const
Definition: footprint.h:235
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
FOOTPRINT * addNewFootprint(COMPONENT *aComponent)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
wxString AsString() const
Definition: kiid.cpp:277
wxString wx_str() const
Definition: utf8.cpp:46
Handle the component boundary box.
Definition: eda_rect.h:42
The main frame for Pcbnew.
The common library.
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Revert the commit by restoring the modified items state.
wxPoint Centre() const
Definition: eda_rect.h:55
const wxString & GetPinName() const
Definition: pcb_netlist.h:57
const std::map< wxString, wxString > & GetProperties() const
Definition: footprint.h:506
std::map< PAD *, wxString > m_padNets
const std::map< wxString, wxString > & GetProperties() const
Definition: pcb_netlist.h:130
const wxString & GetValue() const
Definition: pcb_netlist.h:124
COMPONENT * GetComponentByReference(const wxString &aReference)
Return a COMPONENT by aReference.
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
static REPORTER & GetInstance()
Definition: reporter.cpp:117
void SetProperties(const std::map< wxString, wxString > &aProps)
Definition: footprint.h:507
Definition: pad.h:57
void SetPosition(const wxPoint &aPos) override
Definition: footprint.cpp:1543
bool testConnectivity(NETLIST &aNetlist, std::map< COMPONENT *, FOOTPRINT * > &aFootprintMap)
BOARD_NETLIST_UPDATER(PCB_EDIT_FRAME *aFrame, BOARD *aBoard)
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: footprint.cpp:1274
void cacheNetname(PAD *aPad, const wxString &aNetname)
const KIID_PATH & GetPath() const
Definition: pcb_netlist.h:138
std::map< PAD *, wxString > m_padPinFunctions
static constexpr int Millimeter2iu(double mm)
static const int UNCONNECTED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:365
const wxString & GetPinFunction() const
Definition: pad.h:140
bool IsEmpty() const
Definition: board.h:281
TRACKS & Tracks()
Definition: board.h:230
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
void SetResolveNetConflicts(bool aResolve=true)
Sets a flag that will cause Push() to resolve net conflicts on track/via clusters instead of the defa...
Definition: board_commit.h:63
const std::vector< KIID > & GetKIIDs() const
Definition: pcb_netlist.h:140