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 <[email protected]>
7  * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
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  // This flag is used to prevent connectivity from considering the footprint during its
182  // initial build after the footprint is committed, because we're going to immediately start
183  // a move operation on the footprint and don't want its pads to drive nets onto vias/tracks
184  // it happens to land on at the initial position.
185  footprint->SetAttributes( footprint->GetAttributes() | FP_JUST_ADDED );
186 
187  m_addedFootprints.push_back( footprint );
188  m_commit.Add( footprint );
189 
190  msg.Printf( _( "Added %s (footprint '%s')." ),
191  aComponent->GetReference(),
192  aComponent->GetFPID().Format().wx_str() );
193  }
194 
197  return footprint;
198 }
199 
200 
202  COMPONENT* aNewComponent )
203 {
204  wxString msg;
205 
206  if( aNewComponent->GetFPID().empty() )
207  {
208  msg.Printf( _( "Cannot update %s (no footprint assigned)." ),
209  aNewComponent->GetReference(),
210  aNewComponent->GetFPID().Format().wx_str() );
212  ++m_errorCount;
213  return nullptr;
214  }
215 
216  FOOTPRINT* newFootprint = m_frame->LoadFootprint( aNewComponent->GetFPID() );
217 
218  if( newFootprint == nullptr )
219  {
220  msg.Printf( _( "Cannot update %s (footprint '%s' not found)." ),
221  aNewComponent->GetReference(),
222  aNewComponent->GetFPID().Format().wx_str() );
224  ++m_errorCount;
225  return nullptr;
226  }
227 
228  if( m_isDryRun )
229  {
230  msg.Printf( _( "Change %s footprint from '%s' to '%s'."),
231  aFootprint->GetReference(),
232  aFootprint->GetFPID().Format().wx_str(),
233  aNewComponent->GetFPID().Format().wx_str() );
234 
235  delete newFootprint;
236  newFootprint = nullptr;
237  }
238  else
239  {
240  m_frame->ExchangeFootprint( aFootprint, newFootprint, m_commit );
241 
242  msg.Printf( _( "Changed %s footprint from '%s' to '%s'."),
243  aFootprint->GetReference(),
244  aFootprint->GetFPID().Format().wx_str(),
245  aNewComponent->GetFPID().Format().wx_str() );
246  }
247 
250  return newFootprint;
251 }
252 
253 
255  COMPONENT* aNetlistComponent )
256 {
257  wxString msg;
258 
259  // Create a copy only if the footprint has not been added during this update
260  FOOTPRINT* copy = m_commit.GetStatus( aPcbFootprint ) ? nullptr
261  : (FOOTPRINT*) aPcbFootprint->Clone();
262  bool changed = false;
263 
264  // Test for reference designator field change.
265  if( aPcbFootprint->GetReference() != aNetlistComponent->GetReference() )
266  {
267  if( m_isDryRun )
268  {
269  msg.Printf( _( "Change %s reference designator to %s." ),
270  aPcbFootprint->GetReference(),
271  aNetlistComponent->GetReference() );
272  }
273  else
274  {
275  msg.Printf( _( "Changed %s reference designator to %s." ),
276  aPcbFootprint->GetReference(),
277  aNetlistComponent->GetReference() );
278 
279  changed = true;
280  aPcbFootprint->SetReference( aNetlistComponent->GetReference() );
281  }
282 
284  }
285 
286  // Test for value field change.
287  if( aPcbFootprint->GetValue() != aNetlistComponent->GetValue() )
288  {
289  if( m_isDryRun )
290  {
291  msg.Printf( _( "Change %s value from %s to %s." ),
292  aPcbFootprint->GetReference(),
293  aPcbFootprint->GetValue(),
294  aNetlistComponent->GetValue() );
295  }
296  else
297  {
298  msg.Printf( _( "Changed %s value from %s to %s." ),
299  aPcbFootprint->GetReference(),
300  aPcbFootprint->GetValue(),
301  aNetlistComponent->GetValue() );
302 
303  changed = true;
304  aPcbFootprint->SetValue( aNetlistComponent->GetValue() );
305  }
306 
308  }
309 
310  // Test for time stamp change.
311  KIID_PATH new_path = aNetlistComponent->GetPath();
312 
313  if( !aNetlistComponent->GetKIIDs().empty() )
314  new_path.push_back( aNetlistComponent->GetKIIDs().front() );
315 
316  if( aPcbFootprint->GetPath() != new_path )
317  {
318  if( m_isDryRun )
319  {
320  msg.Printf( _( "Update %s symbol association from %s to %s." ),
321  aPcbFootprint->GetReference(),
322  aPcbFootprint->GetPath().AsString(),
323  new_path.AsString() );
324  }
325  else
326  {
327  msg.Printf( _( "Updated %s symbol association from %s to %s." ),
328  aPcbFootprint->GetReference(),
329  aPcbFootprint->GetPath().AsString(),
330  new_path.AsString() );
331 
332  changed = true;
333  aPcbFootprint->SetPath( new_path );
334  }
335 
337  }
338 
339  if( aPcbFootprint->GetProperties() != aNetlistComponent->GetProperties() )
340  {
341  if( m_isDryRun )
342  {
343  msg.Printf( _( "Update %s properties." ),
344  aPcbFootprint->GetReference() );
345  }
346  else
347  {
348  msg.Printf( _( "Updated %s properties." ),
349  aPcbFootprint->GetReference() );
350 
351  changed = true;
352  aPcbFootprint->SetProperties( aNetlistComponent->GetProperties() );
353  }
354 
356  }
357 
358  if( ( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) > 0 )
359  != ( ( aPcbFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 ) )
360  {
361  if( m_isDryRun )
362  {
363  if( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) )
364  {
365  msg.Printf( _( "Add %s 'exclude from BOM' fabrication attribute." ),
366  aPcbFootprint->GetReference() );
367  }
368  else
369  {
370  msg.Printf( _( "Remove %s 'exclude from BOM' fabrication attribute." ),
371  aPcbFootprint->GetReference() );
372  }
373  }
374  else
375  {
376  int attributes = aPcbFootprint->GetAttributes();
377 
378  if( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) )
379  {
380  attributes |= FP_EXCLUDE_FROM_BOM;
381  msg.Printf( _( "Added %s 'exclude from BOM' fabrication attribute." ),
382  aPcbFootprint->GetReference() );
383  }
384  else
385  {
386  attributes &= ~FP_EXCLUDE_FROM_BOM;
387  msg.Printf( _( "Removed %s 'exclude from BOM' fabrication attribute." ),
388  aPcbFootprint->GetReference() );
389  }
390 
391  changed = true;
392  aPcbFootprint->SetAttributes( attributes );
393  }
394 
396  }
397 
398  if( changed && copy )
399  m_commit.Modified( aPcbFootprint, copy );
400  else
401  delete copy;
402 
403  return true;
404 }
405 
406 
408  COMPONENT* aNewComponent )
409 {
410  wxString msg;
411 
412  // Create a copy only if the footprint has not been added during this update
413  FOOTPRINT* copy = m_commit.GetStatus( aFootprint ) ? nullptr : (FOOTPRINT*) aFootprint->Clone();
414  bool changed = false;
415 
416  // At this point, the component footprint is updated. Now update the nets.
417  for( PAD* pad : aFootprint->Pads() )
418  {
419  const COMPONENT_NET& net = aNewComponent->GetNet( pad->GetNumber() );
420 
421  wxString pinFunction;
422  wxString pinType;
423 
424  if( net.IsValid() ) // i.e. the pad has a name
425  {
426  pinFunction = net.GetPinFunction();
427  pinType = net.GetPinType();
428  }
429 
430  if( !m_isDryRun )
431  {
432  if( pad->GetPinFunction() != pinFunction )
433  {
434  changed = true;
435  pad->SetPinFunction( pinFunction );
436  }
437 
438  if( pad->GetPinType() != pinType )
439  {
440  changed = true;
441  pad->SetPinType( pinType );
442  }
443  }
444  else
445  {
446  cachePinFunction( pad, pinFunction );
447  }
448 
449  // Test if new footprint pad has no net (pads not on copper layers have no net).
450  if( !net.IsValid() || !pad->IsOnCopperLayer() )
451  {
452  if( !pad->GetNetname().IsEmpty() )
453  {
454  if( m_isDryRun )
455  {
456  msg.Printf( _( "Disconnect %s pin %s." ),
457  aFootprint->GetReference(),
458  pad->GetNumber() );
459  }
460  else
461  {
462  msg.Printf( _( "Disconnected %s pin %s." ),
463  aFootprint->GetReference(),
464  pad->GetNumber() );
465  }
466 
468  }
469  else if( pad->IsOnCopperLayer() && !pad->GetNumber().IsEmpty() )
470  {
471  // pad is connectable but has no net found in netlist
472  msg.Printf( _( "No net found for symbol %s pin %s." ),
473  aFootprint->GetReference(),
474  pad->GetNumber() );
476  }
477 
478  if( !m_isDryRun )
479  {
480  changed = true;
481  pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
482 
483  // If the pad has no net from netlist (i.e. not in netlist
484  // it cannot have a pin function
485  if( pad->GetNetname().IsEmpty() )
486  pad->SetPinFunction( wxEmptyString );
487 
488  }
489  else
490  {
491  cacheNetname( pad, wxEmptyString );
492  }
493  }
494  else // New footprint pad has a net.
495  {
496  const wxString& netName = net.GetNetName();
497  NETINFO_ITEM* netinfo = m_board->FindNet( netName );
498 
499  if( netinfo && !m_isDryRun )
500  netinfo->SetIsCurrent( true );
501 
502  if( pad->GetNetname() != netName )
503  {
504 
505  if( netinfo == nullptr )
506  {
507  // It might be a new net that has not been added to the board yet
508  if( m_addedNets.count( netName ) )
509  netinfo = m_addedNets[ netName ];
510  }
511 
512  if( netinfo == nullptr )
513  {
514  netinfo = new NETINFO_ITEM( m_board, netName );
515 
516  // It is a new net, we have to add it
517  if( !m_isDryRun )
518  {
519  changed = true;
520  m_commit.Add( netinfo );
521  }
522 
523  m_addedNets[netName] = netinfo;
524  msg.Printf( _( "Add net %s." ), UnescapeString( netName ) );
526  }
527 
528  if( !pad->GetNetname().IsEmpty() )
529  {
530  m_oldToNewNets[ pad->GetNetname() ] = netName;
531 
532  if( m_isDryRun )
533  {
534  msg.Printf( _( "Reconnect %s pin %s from %s to %s."),
535  aFootprint->GetReference(),
536  pad->GetNumber(),
537  UnescapeString( pad->GetNetname() ),
538  UnescapeString( netName ) );
539  }
540  else
541  {
542  msg.Printf( _( "Reconnected %s pin %s from %s to %s."),
543  aFootprint->GetReference(),
544  pad->GetNumber(),
545  UnescapeString( pad->GetNetname() ),
546  UnescapeString( netName ) );
547  }
548  }
549  else
550  {
551  if( m_isDryRun )
552  {
553  msg.Printf( _( "Connect %s pin %s to %s."),
554  aFootprint->GetReference(),
555  pad->GetNumber(),
556  UnescapeString( netName ) );
557  }
558  else
559  {
560  msg.Printf( _( "Connected %s pin %s to %s."),
561  aFootprint->GetReference(),
562  pad->GetNumber(),
563  UnescapeString( netName ) );
564  }
565  }
566 
568 
569  if( !m_isDryRun )
570  {
571  changed = true;
572  pad->SetNet( netinfo );
573  }
574  else
575  {
576  cacheNetname( pad, netName );
577  }
578  }
579  }
580  }
581 
582  if( changed && copy )
583  m_commit.Modified( aFootprint, copy );
584  else
585  delete copy;
586 
587  return true;
588 }
589 
590 
592 {
593  for( ZONE* zone : m_board->Zones() )
594  {
595  if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
596  continue;
597 
598  m_zoneConnectionsCache[ zone ] = m_board->GetConnectivity()->GetConnectedPads( zone );
599  }
600 }
601 
602 
604 {
605  wxString msg;
606  std::set<wxString> netlistNetnames;
607 
608  for( int ii = 0; ii < (int) aNetlist.GetCount(); ii++ )
609  {
610  const COMPONENT* component = aNetlist.GetComponent( ii );
611 
612  for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
613  {
614  const COMPONENT_NET& net = component->GetNet( jj );
615  netlistNetnames.insert( net.GetNetName() );
616  }
617  }
618 
619  for( PCB_TRACK* via : m_board->Tracks() )
620  {
621  if( via->Type() != PCB_VIA_T )
622  continue;
623 
624  if( netlistNetnames.count( via->GetNetname() ) == 0 )
625  {
626  wxString updatedNetname = wxEmptyString;
627 
628  // Take via name from name change map if it didn't match to a new pad
629  // (this is useful for stitching vias that don't connect to tracks)
630  if( m_oldToNewNets.count( via->GetNetname() ) )
631  {
632  updatedNetname = m_oldToNewNets[via->GetNetname()];
633  }
634 
635  if( !updatedNetname.IsEmpty() )
636  {
637  if( m_isDryRun )
638  {
639  msg.Printf( _( "Reconnect via from %s to %s." ),
640  UnescapeString( via->GetNetname() ),
641  UnescapeString( updatedNetname ) );
642 
644  }
645  else
646  {
647  NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
648 
649  if( !netinfo )
650  netinfo = m_addedNets[updatedNetname];
651 
652  if( netinfo )
653  {
654  m_commit.Modify( via );
655  via->SetNet( netinfo );
656 
657  msg.Printf( _( "Reconnected via from %s to %s." ),
658  UnescapeString( via->GetNetname() ),
659  UnescapeString( updatedNetname ) );
660 
662  }
663  }
664  }
665  else
666  {
667  msg.Printf( _( "Via connected to unknown net (%s)." ),
668  UnescapeString( via->GetNetname() ) );
670  ++m_warningCount;
671  }
672  }
673  }
674 
675  // Test copper zones to detect "dead" nets (nets without any pad):
676  for( ZONE* zone : m_board->Zones() )
677  {
678  if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
679  continue;
680 
681  if( netlistNetnames.count( zone->GetNetname() ) == 0 )
682  {
683  // Look for a pad in the zone's connected-pad-cache which has been updated to
684  // a new net and use that. While this won't always be the right net, the dead
685  // net is guaranteed to be wrong.
686  wxString updatedNetname = wxEmptyString;
687 
688  for( PAD* pad : m_zoneConnectionsCache[ zone ] )
689  {
690  if( getNetname( pad ) != zone->GetNetname() )
691  {
692  updatedNetname = getNetname( pad );
693  break;
694  }
695  }
696 
697  // Take zone name from name change map if it didn't match to a new pad
698  // (this is useful for zones on internal layers)
699  if( updatedNetname.IsEmpty() && m_oldToNewNets.count( zone->GetNetname() ) )
700  {
701  updatedNetname = m_oldToNewNets[ zone->GetNetname() ];
702  }
703 
704  if( !updatedNetname.IsEmpty() )
705  {
706  if( m_isDryRun )
707  {
708  if( !zone->GetZoneName().IsEmpty() )
709  {
710  msg.Printf( _( "Reconnect copper zone '%s' from %s to %s." ),
711  zone->GetZoneName(),
712  UnescapeString( zone->GetNetname() ),
713  UnescapeString( updatedNetname ) );
714  }
715  else
716  {
717  msg.Printf( _( "Reconnect copper zone from %s to %s." ),
718  UnescapeString( zone->GetNetname() ),
719  UnescapeString( updatedNetname ) );
720  }
721 
723  }
724  else
725  {
726  NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
727 
728  if( !netinfo )
729  netinfo = m_addedNets[ updatedNetname ];
730 
731  if( netinfo )
732  {
733  m_commit.Modify( zone );
734  zone->SetNet( netinfo );
735 
736  if( !zone->GetZoneName().IsEmpty() )
737  {
738  msg.Printf( _( "Reconnected copper zone '%s' from %s to %s." ),
739  zone->GetZoneName(),
740  UnescapeString( zone->GetNetname() ),
741  UnescapeString( updatedNetname ) );
742  }
743  else
744  {
745  msg.Printf( _( "Reconnected copper zone from %s to %s." ),
746  UnescapeString( zone->GetNetname() ),
747  UnescapeString( updatedNetname ) );
748  }
749 
751  }
752  }
753  }
754  else
755  {
756  if( !zone->GetZoneName().IsEmpty() )
757  {
758  msg.Printf( _( "Copper zone '%s' has no pads connected." ),
759  zone->GetZoneName() );
760  }
761  else
762  {
763  PCB_LAYER_ID layer = zone->GetLayer();
764  wxPoint pos = zone->GetPosition();
765 
766  msg.Printf( _( "Copper zone on layer %s at (%s, %s) has no pads connected." ),
767  m_board->GetLayerName( layer ),
770  }
771 
773  ++m_warningCount;
774  }
775  }
776  }
777 
778  return true;
779 }
780 
781 
783  std::map<COMPONENT*, FOOTPRINT*>& aFootprintMap )
784 {
785  // Verify that board contains all pads in netlist: if it doesn't then footprints are
786  // wrong or missing.
787 
788  wxString msg;
789  wxString padNumber;
790 
791  for( int i = 0; i < (int) aNetlist.GetCount(); i++ )
792  {
793  COMPONENT* component = aNetlist.GetComponent( i );
794  FOOTPRINT* footprint = aFootprintMap[component];
795 
796  if( !footprint ) // It can be missing in partial designs
797  continue;
798 
799  // Explore all pins/pads in component
800  for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
801  {
802  padNumber = component->GetNet( jj ).GetPinName();
803 
804  if( footprint->FindPadByNumber( padNumber ) )
805  continue; // OK, pad found
806 
807  // not found: bad footprint, report error
808  msg.Printf( _( "%s pad %s not found in %s." ),
809  component->GetReference(),
810  padNumber,
811  footprint->GetFPID().Format().wx_str() );
813  ++m_errorCount;
814  }
815  }
816 
817  return true;
818 }
819 
820 
822 {
823  FOOTPRINT* lastPreexistingFootprint = nullptr;
824  COMPONENT* component = nullptr;
825  wxString msg;
826 
827  m_errorCount = 0;
828  m_warningCount = 0;
830 
831  std::map<COMPONENT*, FOOTPRINT*> footprintMap;
832 
833  if( !m_board->Footprints().empty() )
834  lastPreexistingFootprint = m_board->Footprints().back();
835 
837 
838  // First mark all nets (except <no net>) as stale; we'll update those which are current
839  // in the following two loops.
840  //
841  if( !m_isDryRun )
842  {
843  m_board->SetStatus( 0 );
844 
845  for( NETINFO_ITEM* net : m_board->GetNetInfo() )
846  net->SetIsCurrent( net->GetNetCode() == 0 );
847  }
848 
849  // Next go through the netlist updating all board footprints which have matching component
850  // entries and adding new footprints for those that don't.
851  //
852  for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
853  {
854  component = aNetlist.GetComponent( i );
855 
856  if( component->GetProperties().count( wxT( "exclude_from_board" ) ) )
857  continue;
858 
859  msg.Printf( _( "Processing symbol '%s:%s'." ),
860  component->GetReference(),
861  component->GetFPID().Format().wx_str() );
863 
864  int matchCount = 0;
865 
866  for( FOOTPRINT* footprint : m_board->Footprints() )
867  {
868  bool match = false;
869 
870  if( m_lookupByTimestamp )
871  {
872  for( const KIID& uuid : component->GetKIIDs() )
873  {
874  KIID_PATH base = component->GetPath();
875  base.push_back( uuid );
876 
877  if( footprint->GetPath() == base )
878  {
879  match = true;
880  break;
881  }
882  }
883  }
884  else
885  {
886  match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
887  }
888 
889  if( match )
890  {
891  FOOTPRINT* tmp = footprint;
892 
893  if( m_replaceFootprints && component->GetFPID() != footprint->GetFPID() )
894  tmp = replaceFootprint( aNetlist, footprint, component );
895 
896  if( tmp )
897  {
898  footprintMap[ component ] = tmp;
899 
900  updateFootprintParameters( tmp, component );
901  updateComponentPadConnections( tmp, component );
902  }
903 
904  matchCount++;
905  }
906 
907  if( footprint == lastPreexistingFootprint )
908  {
909  // No sense going through the newly-created footprints: end of loop
910  break;
911  }
912  }
913 
914  if( matchCount == 0 )
915  {
916  FOOTPRINT* footprint = addNewFootprint( component );
917 
918  if( footprint )
919  {
920  footprintMap[ component ] = footprint;
921 
922  updateFootprintParameters( footprint, component );
923  updateComponentPadConnections( footprint, component );
924  }
925  }
926  else if( matchCount > 1 )
927  {
928  msg.Printf( _( "Multiple footprints found for '%s'." ), component->GetReference() );
930  }
931  }
932 
933  updateCopperZoneNets( aNetlist );
934 
935  // Finally go through the board footprints and update all those that *don't* have matching
936  // component entries.
937  //
938  for( FOOTPRINT* footprint : m_board->Footprints() )
939  {
940  bool matched = false;
941  bool doDelete = m_deleteUnusedFootprints;
942 
943  if( ( footprint->GetAttributes() & FP_BOARD_ONLY ) > 0 )
944  doDelete = false;
945 
946  if( m_lookupByTimestamp )
947  component = aNetlist.GetComponentByPath( footprint->GetPath() );
948  else
949  component = aNetlist.GetComponentByReference( footprint->GetReference() );
950 
951  if( component && component->GetProperties().count( wxT( "exclude_from_board" ) ) == 0 )
952  matched = true;
953 
954  if( doDelete && !matched && footprint->IsLocked() )
955  {
956  if( m_isDryRun )
957  {
958  msg.Printf( _( "Cannot remove unused footprint %s (locked)." ),
959  footprint->GetReference() );
960  }
961  else
962  {
963  msg.Printf( _( "Could not remove unused footprint %s (locked)." ),
964  footprint->GetReference() );
965  }
966 
968  doDelete = false;
969  }
970 
971  if( doDelete && !matched )
972  {
973  if( m_isDryRun )
974  {
975  msg.Printf( _( "Remove unused footprint %s." ), footprint->GetReference() );
976  }
977  else
978  {
979  m_commit.Remove( footprint );
980  msg.Printf( _( "Removed unused footprint %s." ), footprint->GetReference() );
981  }
982 
984  }
985  else if( !m_isDryRun )
986  {
987  if( !matched )
988  footprint->SetPath( KIID_PATH() );
989 
990  for( PAD* pad : footprint->Pads() )
991  {
992  if( pad->GetNet() )
993  pad->GetNet()->SetIsCurrent( true );
994  }
995  }
996  }
997 
998  if( !m_isDryRun )
999  {
1000  m_board->GetConnectivity()->Build( m_board );
1001  testConnectivity( aNetlist, footprintMap );
1002 
1003  for( NETINFO_ITEM* net : m_board->GetNetInfo() )
1004  {
1005  if( !net->IsCurrent() )
1006  {
1007  msg.Printf( _( "Removed unused net %s." ), net->GetNetname() );
1009  m_commit.Removed( net );
1010  }
1011  }
1012 
1015  m_commit.Push( _( "Update netlist" ) );
1016 
1019  }
1020 
1021  if( m_isDryRun )
1022  {
1023  for( const std::pair<const wxString, NETINFO_ITEM*>& addedNet : m_addedNets )
1024  delete addedNet.second;
1025 
1026  m_addedNets.clear();
1027  }
1028 
1029  // Update the ratsnest
1030  m_reporter->ReportTail( wxEmptyString, RPT_SEVERITY_ACTION );
1031  m_reporter->ReportTail( wxEmptyString, RPT_SEVERITY_ACTION );
1032 
1033  msg.Printf( _( "Total warnings: %d, errors: %d." ), m_warningCount, m_errorCount );
1035 
1036  return true;
1037 }
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:1328
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:536
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:240
const wxString & GetValue() const
Definition: footprint.h:485
const KIID_PATH & GetPath() const
Definition: footprint.h:204
wxString getNetname(PAD *aPad)
void SetPath(const KIID_PATH &aPath)
Definition: footprint.h:205
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:738
void SetIsCurrent(bool isCurrent)
Definition: netinfo.h:148
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:118
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:680
void SetStatus(EDA_ITEM_FLAGS aStatus)
Definition: eda_item.h:150
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:115
void SetAttributes(int aAttributes)
Definition: footprint.h:237
PADS & Pads()
Definition: footprint.h:169
int GetBottom() const
Definition: eda_rect.h:123
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:1394
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:234
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:345
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
Definition: footprint.cpp:1075
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:195
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:119
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:66
bool updateFootprintParameters(FOOTPRINT *aPcbFootprint, COMPONENT *aNetlistComponent)
int GetAttributes() const
Definition: footprint.h:236
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
FOOTPRINT * addNewFootprint(COMPONENT *aComponent)
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
wxString AsString() const
Definition: kiid.cpp:316
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:64
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:1562
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:1293
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:372
const wxString & GetPinFunction() const
Definition: pad.h:140
bool IsEmpty() const
Definition: board.h:282
TRACKS & Tracks()
Definition: board.h:231
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:66
const std::vector< KIID > & GetKIIDs() const
Definition: pcb_netlist.h:140