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 <kicad_string.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 
55  m_deleteSinglePadNets = true;
57  m_isDryRun = false;
58  m_replaceFootprints = true;
59  m_lookupByTimestamp = false;
60  m_warnForNoNetPads = false;
61 
62  m_warningCount = 0;
63  m_errorCount = 0;
65 }
66 
67 
69 {
70 }
71 
72 
73 // These functions allow inspection of pad nets during dry runs by keeping a cache of
74 // current pad netnames indexed by pad.
75 
76 void BOARD_NETLIST_UPDATER::cacheNetname( PAD* aPad, const wxString& aNetname )
77 {
78  m_padNets[ aPad ] = aNetname;
79 }
80 
81 
83 {
84  if( m_isDryRun && m_padNets.count( aPad ) )
85  return m_padNets[ aPad ];
86  else
87  return aPad->GetNetname();
88 }
89 
90 
91 void BOARD_NETLIST_UPDATER::cachePinFunction( PAD* aPad, const wxString& aPinFunction )
92 {
93  m_padPinFunctions[ aPad ] = aPinFunction;
94 }
95 
96 
98 {
99  if( m_isDryRun && m_padPinFunctions.count( aPad ) )
100  return m_padPinFunctions[ aPad ];
101  else
102  return aPad->GetPinFunction();
103 }
104 
105 
107 {
108  wxPoint bestPosition;
109 
110  if( !m_board->IsEmpty() )
111  {
112  // Position new components below any existing board features.
114 
115  if( bbox.GetWidth() || bbox.GetHeight() )
116  {
117  bestPosition.x = bbox.Centre().x;
118  bestPosition.y = bbox.GetBottom() + Millimeter2iu( 10 );
119  }
120  }
121  else
122  {
123  // Position new components in the center of the page when the board is empty.
124  wxSize pageSize = m_board->GetPageSettings().GetSizeIU();
125 
126  bestPosition.x = pageSize.GetWidth() / 2;
127  bestPosition.y = pageSize.GetHeight() / 2;
128  }
129 
130  return bestPosition;
131 }
132 
133 
135 {
136  wxString msg;
137 
138  if( aComponent->GetFPID().empty() )
139  {
140  msg.Printf( _( "Cannot add %s (no footprint assigned)." ),
141  aComponent->GetReference(),
142  aComponent->GetFPID().Format().wx_str() );
144  ++m_errorCount;
145  return nullptr;
146  }
147 
148  FOOTPRINT* footprint = m_frame->LoadFootprint( aComponent->GetFPID() );
149 
150  if( footprint == nullptr )
151  {
152  msg.Printf( _( "Cannot add %s (footprint '%s' not found)." ),
153  aComponent->GetReference(),
154  aComponent->GetFPID().Format().wx_str() );
156  ++m_errorCount;
157  return nullptr;
158  }
159 
160  if( m_isDryRun )
161  {
162  msg.Printf( _( "Add %s (footprint '%s')." ),
163  aComponent->GetReference(),
164  aComponent->GetFPID().Format().wx_str() );
165 
166  delete footprint;
167  footprint = nullptr;
168  }
169  else
170  {
171  for( PAD* pad : footprint->Pads() )
172  {
173  // Set the pads ratsnest settings to the global settings
174  pad->SetLocalRatsnestVisible( m_frame->GetDisplayOptions().m_ShowGlobalRatsnest );
175 
176  // Pads in the library all have orphaned nets. Replace with Default.
177  pad->SetNetCode( 0 );
178  }
179 
180  footprint->SetParent( m_board );
182 
183  m_addedFootprints.push_back( footprint );
184  m_commit.Add( footprint );
185 
186  msg.Printf( _( "Added %s (footprint '%s')." ),
187  aComponent->GetReference(),
188  aComponent->GetFPID().Format().wx_str() );
189  }
190 
193  return footprint;
194 }
195 
196 
198  COMPONENT* aNewComponent )
199 {
200  wxString msg;
201 
202  if( aNewComponent->GetFPID().empty() )
203  {
204  msg.Printf( _( "Cannot update %s (no footprint assigned)." ),
205  aNewComponent->GetReference(),
206  aNewComponent->GetFPID().Format().wx_str() );
208  ++m_errorCount;
209  return nullptr;
210  }
211 
212  FOOTPRINT* newFootprint = m_frame->LoadFootprint( aNewComponent->GetFPID() );
213 
214  if( newFootprint == nullptr )
215  {
216  msg.Printf( _( "Cannot update %s (footprint '%s' not found)." ),
217  aNewComponent->GetReference(),
218  aNewComponent->GetFPID().Format().wx_str() );
220  ++m_errorCount;
221  return nullptr;
222  }
223 
224  if( m_isDryRun )
225  {
226  msg.Printf( _( "Change %s footprint from '%s' to '%s'."),
227  aFootprint->GetReference(),
228  aFootprint->GetFPID().Format().wx_str(),
229  aNewComponent->GetFPID().Format().wx_str() );
230 
231  delete newFootprint;
232  newFootprint = nullptr;
233  }
234  else
235  {
236  m_frame->ExchangeFootprint( aFootprint, newFootprint, m_commit );
237 
238  msg.Printf( _( "Changed %s footprint from '%s' to '%s'."),
239  aFootprint->GetReference(),
240  aFootprint->GetFPID().Format().wx_str(),
241  aNewComponent->GetFPID().Format().wx_str() );
242  }
243 
246  return newFootprint;
247 }
248 
249 
251  COMPONENT* aNetlistComponent )
252 {
253  wxString msg;
254 
255  // Create a copy only if the footprint has not been added during this update
256  FOOTPRINT* copy = m_commit.GetStatus( aPcbFootprint ) ? nullptr
257  : (FOOTPRINT*) aPcbFootprint->Clone();
258  bool changed = false;
259 
260  // Test for reference designator field change.
261  if( aPcbFootprint->GetReference() != aNetlistComponent->GetReference() )
262  {
263  if( m_isDryRun )
264  {
265  msg.Printf( _( "Change %s reference designator to %s." ),
266  aPcbFootprint->GetReference(),
267  aNetlistComponent->GetReference() );
268  }
269  else
270  {
271  msg.Printf( _( "Changed %s reference designator to %s." ),
272  aPcbFootprint->GetReference(),
273  aNetlistComponent->GetReference() );
274 
275  changed = true;
276  aPcbFootprint->SetReference( aNetlistComponent->GetReference() );
277  }
278 
280  }
281 
282  // Test for value field change.
283  if( aPcbFootprint->GetValue() != aNetlistComponent->GetValue() )
284  {
285  if( m_isDryRun )
286  {
287  msg.Printf( _( "Change %s value from %s to %s." ),
288  aPcbFootprint->GetReference(),
289  aPcbFootprint->GetValue(),
290  aNetlistComponent->GetValue() );
291  }
292  else
293  {
294  msg.Printf( _( "Changed %s value from %s to %s." ),
295  aPcbFootprint->GetReference(),
296  aPcbFootprint->GetValue(),
297  aNetlistComponent->GetValue() );
298 
299  changed = true;
300  aPcbFootprint->SetValue( aNetlistComponent->GetValue() );
301  }
302 
304  }
305 
306  // Test for time stamp change.
307  KIID_PATH new_path = aNetlistComponent->GetPath();
308 
309  if( !aNetlistComponent->GetKIIDs().empty() )
310  new_path.push_back( aNetlistComponent->GetKIIDs().front() );
311 
312  if( aPcbFootprint->GetPath() != new_path )
313  {
314  if( m_isDryRun )
315  {
316  msg.Printf( _( "Update %s symbol association from %s to %s." ),
317  aPcbFootprint->GetReference(),
318  aPcbFootprint->GetPath().AsString(),
319  new_path.AsString() );
320  }
321  else
322  {
323  msg.Printf( _( "Updated %s symbol association from %s to %s." ),
324  aPcbFootprint->GetReference(),
325  aPcbFootprint->GetPath().AsString(),
326  new_path.AsString() );
327 
328  changed = true;
329  aPcbFootprint->SetPath( new_path );
330  }
331 
333  }
334 
335  if( aPcbFootprint->GetProperties() != aNetlistComponent->GetProperties() )
336  {
337  if( m_isDryRun )
338  {
339  msg.Printf( _( "Update %s properties." ),
340  aPcbFootprint->GetReference() );
341  }
342  else
343  {
344  msg.Printf( _( "Updated %s properties." ),
345  aPcbFootprint->GetReference() );
346 
347  changed = true;
348  aPcbFootprint->SetProperties( aNetlistComponent->GetProperties() );
349  }
350 
352  }
353 
354  if( ( aNetlistComponent->GetProperties().count( "exclude_from_bom" ) > 0 )
355  != ( ( aPcbFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 ) )
356  {
357  if( m_isDryRun )
358  {
359  if( aNetlistComponent->GetProperties().count( "exclude_from_bom" ) )
360  {
361  msg.Printf( _( "Add %s 'exclude from BOM' fabrication attribute." ),
362  aPcbFootprint->GetReference() );
363  }
364  else
365  {
366  msg.Printf( _( "Remove %s 'exclude from BOM' fabrication attribute." ),
367  aPcbFootprint->GetReference() );
368  }
369  }
370  else
371  {
372  int attributes = aPcbFootprint->GetAttributes();
373 
374  if( aNetlistComponent->GetProperties().count( "exclude_from_bom" ) )
375  {
376  attributes |= FP_EXCLUDE_FROM_BOM;
377  msg.Printf( _( "Added %s 'exclude from BOM' fabrication attribute." ),
378  aPcbFootprint->GetReference() );
379  }
380  else
381  {
382  attributes &= ~FP_EXCLUDE_FROM_BOM;
383  msg.Printf( _( "Removed %s 'exclude from BOM' fabrication attribute." ),
384  aPcbFootprint->GetReference() );
385  }
386 
387  changed = true;
388  aPcbFootprint->SetAttributes( attributes );
389  }
390 
392  }
393 
394  if( changed && copy )
395  m_commit.Modified( aPcbFootprint, copy );
396  else
397  delete copy;
398 
399  return true;
400 }
401 
402 
404  COMPONENT* aNewComponent )
405 {
406  wxString msg;
407 
408  // Create a copy only if the footprint has not been added during this update
409  FOOTPRINT* copy = m_commit.GetStatus( aFootprint ) ? nullptr : (FOOTPRINT*) aFootprint->Clone();
410  bool changed = false;
411 
412  // At this point, the component footprint is updated. Now update the nets.
413  for( PAD* pad : aFootprint->Pads() )
414  {
415  const COMPONENT_NET& net = aNewComponent->GetNet( pad->GetName() );
416 
417  wxString pinFunction;
418  wxString pinType;
419 
420  if( net.IsValid() ) // i.e. the pad has a name
421  {
422  pinFunction = net.GetPinFunction();
423  pinType = net.GetPinType();
424  }
425 
426  if( !m_isDryRun )
427  {
428  if( pad->GetPinFunction() != pinFunction )
429  {
430  changed = true;
431  pad->SetPinFunction( pinFunction );
432  }
433 
434  if( pad->GetPinType() != pinType )
435  {
436  changed = true;
437  pad->SetPinType( pinType );
438  }
439  }
440  else
441  {
442  cachePinFunction( pad, pinFunction );
443  }
444 
445  // Test if new footprint pad has no net (pads not on copper layers have no net).
446  if( !net.IsValid() || !pad->IsOnCopperLayer() )
447  {
448  if( !pad->GetNetname().IsEmpty() )
449  {
450  if( m_isDryRun )
451  {
452  msg.Printf( _( "Disconnect %s pin %s." ),
453  aFootprint->GetReference(),
454  pad->GetName() );
455  }
456  else
457  {
458  msg.Printf( _( "Disconnected %s pin %s." ),
459  aFootprint->GetReference(),
460  pad->GetName() );
461  }
462 
464  }
465  else if( m_warnForNoNetPads && pad->IsOnCopperLayer() && !pad->GetName().IsEmpty() )
466  {
467  // pad is connectable but has no net found in netlist
468  msg.Printf( _( "No net found for symbol %s pin %s." ),
469  aFootprint->GetReference(),
470  pad->GetName() );
472  }
473 
474  if( !m_isDryRun )
475  {
476  changed = true;
477  pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
478 
479  // If the pad has no net from netlist (i.e. not in netlist
480  // it cannot have a pin function
481  if( pad->GetNetname().IsEmpty() )
482  pad->SetPinFunction( wxEmptyString );
483 
484  }
485  else
486  {
487  cacheNetname( pad, wxEmptyString );
488  }
489  }
490  else // New footprint pad has a net.
491  {
492  const wxString& netName = net.GetNetName();
493  NETINFO_ITEM* netinfo = m_board->FindNet( netName );
494 
495  if( netinfo && !m_isDryRun )
496  netinfo->SetIsCurrent( true );
497 
498  if( pad->GetNetname() != netName )
499  {
500 
501  if( netinfo == nullptr )
502  {
503  // It might be a new net that has not been added to the board yet
504  if( m_addedNets.count( netName ) )
505  netinfo = m_addedNets[ netName ];
506  }
507 
508  if( netinfo == nullptr )
509  {
510  netinfo = new NETINFO_ITEM( m_board, netName );
511 
512  // It is a new net, we have to add it
513  if( !m_isDryRun )
514  {
515  changed = true;
516  m_commit.Add( netinfo );
517  }
518 
519  m_addedNets[netName] = netinfo;
520  msg.Printf( _( "Add net %s." ), UnescapeString( netName ) );
522  }
523 
524  if( !pad->GetNetname().IsEmpty() )
525  {
526  m_oldToNewNets[ pad->GetNetname() ] = netName;
527 
528  if( m_isDryRun )
529  {
530  msg.Printf( _( "Reconnect %s pin %s from %s to %s."),
531  aFootprint->GetReference(),
532  pad->GetName(),
533  UnescapeString( pad->GetNetname() ),
534  UnescapeString( netName ) );
535  }
536  else
537  {
538  msg.Printf( _( "Reconnected %s pin %s from %s to %s."),
539  aFootprint->GetReference(),
540  pad->GetName(),
541  UnescapeString( pad->GetNetname() ),
542  UnescapeString( netName ) );
543  }
544  }
545  else
546  {
547  if( m_isDryRun )
548  {
549  msg.Printf( _( "Connect %s pin %s to %s."),
550  aFootprint->GetReference(),
551  pad->GetName(),
552  UnescapeString( netName ) );
553  }
554  else
555  {
556  msg.Printf( _( "Connected %s pin %s to %s."),
557 
558  aFootprint->GetReference(),
559  pad->GetName(),
560  UnescapeString( netName ) );
561  }
562  }
563 
565 
566  if( !m_isDryRun )
567  {
568  changed = true;
569  pad->SetNet( netinfo );
570  }
571  else
572  {
573  cacheNetname( pad, netName );
574  }
575  }
576  }
577  }
578 
579  if( changed && copy )
580  m_commit.Modified( aFootprint, copy );
581  else
582  delete copy;
583 
584  return true;
585 }
586 
587 
589 {
590  for( ZONE* zone : m_board->Zones() )
591  {
592  if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
593  continue;
594 
595  m_zoneConnectionsCache[ zone ] = m_board->GetConnectivity()->GetConnectedPads( zone );
596  }
597 }
598 
599 
601 {
602  wxString msg;
603  std::set<wxString> netlistNetnames;
604 
605  for( int ii = 0; ii < (int) aNetlist.GetCount(); ii++ )
606  {
607  const COMPONENT* component = aNetlist.GetComponent( ii );
608 
609  for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
610  {
611  const COMPONENT_NET& net = component->GetNet( jj );
612  netlistNetnames.insert( net.GetNetName() );
613  }
614  }
615 
616  for( PCB_TRACK* via : m_board->Tracks() )
617  {
618  if( via->Type() != PCB_VIA_T )
619  continue;
620 
621  if( netlistNetnames.count( via->GetNetname() ) == 0 )
622  {
623  wxString updatedNetname = wxEmptyString;
624 
625  // Take via name from name change map if it didn't match to a new pad
626  // (this is useful for stitching vias that don't connect to tracks)
627  if( m_oldToNewNets.count( via->GetNetname() ) )
628  {
629  updatedNetname = m_oldToNewNets[via->GetNetname()];
630  }
631 
632  if( !updatedNetname.IsEmpty() )
633  {
634  if( m_isDryRun )
635  {
636  msg.Printf( _( "Reconnect via from %s to %s." ),
637  UnescapeString( via->GetNetname() ),
638  UnescapeString( updatedNetname ) );
639 
641  }
642  else
643  {
644  NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
645 
646  if( !netinfo )
647  netinfo = m_addedNets[updatedNetname];
648 
649  if( netinfo )
650  {
651  m_commit.Modify( via );
652  via->SetNet( netinfo );
653 
654  msg.Printf( _( "Reconnected via from %s to %s." ),
655  UnescapeString( via->GetNetname() ),
656  UnescapeString( updatedNetname ) );
657 
659  }
660  }
661  }
662  else
663  {
664  msg.Printf( _( "Via connected to unknown net (%s)." ),
665  UnescapeString( via->GetNetname() ) );
667  ++m_warningCount;
668  }
669  }
670  }
671 
672  // Test copper zones to detect "dead" nets (nets without any pad):
673  for( ZONE* zone : m_board->Zones() )
674  {
675  if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
676  continue;
677 
678  if( netlistNetnames.count( zone->GetNetname() ) == 0 )
679  {
680  // Look for a pad in the zone's connected-pad-cache which has been updated to
681  // a new net and use that. While this won't always be the right net, the dead
682  // net is guaranteed to be wrong.
683  wxString updatedNetname = wxEmptyString;
684 
685  for( PAD* pad : m_zoneConnectionsCache[ zone ] )
686  {
687  if( getNetname( pad ) != zone->GetNetname() )
688  {
689  updatedNetname = getNetname( pad );
690  break;
691  }
692  }
693 
694  // Take zone name from name change map if it didn't match to a new pad
695  // (this is useful for zones on internal layers)
696  if( updatedNetname.IsEmpty() && m_oldToNewNets.count( zone->GetNetname() ) )
697  {
698  updatedNetname = m_oldToNewNets[ zone->GetNetname() ];
699  }
700 
701  if( !updatedNetname.IsEmpty() )
702  {
703  if( m_isDryRun )
704  {
705  if( !zone->GetZoneName().IsEmpty() )
706  {
707  msg.Printf( _( "Reconnect copper zone '%s' from %s to %s." ),
708  zone->GetZoneName(),
709  UnescapeString( zone->GetNetname() ),
710  UnescapeString( updatedNetname ) );
711  }
712  else
713  {
714  msg.Printf( _( "Reconnect copper zone from %s to %s." ),
715  UnescapeString( zone->GetNetname() ),
716  UnescapeString( updatedNetname ) );
717  }
718 
720  }
721  else
722  {
723  NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
724 
725  if( !netinfo )
726  netinfo = m_addedNets[ updatedNetname ];
727 
728  if( netinfo )
729  {
730  m_commit.Modify( zone );
731  zone->SetNet( netinfo );
732 
733  if( !zone->GetZoneName().IsEmpty() )
734  {
735  msg.Printf( _( "Reconnected copper zone '%s' from %s to %s." ),
736  zone->GetZoneName(),
737  UnescapeString( zone->GetNetname() ),
738  UnescapeString( updatedNetname ) );
739  }
740  else
741  {
742  msg.Printf( _( "Reconnected copper zone from %s to %s." ),
743  UnescapeString( zone->GetNetname() ),
744  UnescapeString( updatedNetname ) );
745  }
746 
748  }
749  }
750  }
751  else
752  {
753  if( !zone->GetZoneName().IsEmpty() )
754  {
755  msg.Printf( _( "Copper zone '%s' has no pads connected." ),
756  zone->GetZoneName() );
757  }
758  else
759  {
760  PCB_LAYER_ID layer = zone->GetLayer();
761  wxPoint pos = zone->GetPosition();
762 
763  msg.Printf( _( "Copper zone on layer %s at (%s, %s) has no pads connected." ),
764  m_board->GetLayerName( layer ),
767  }
768 
770  ++m_warningCount;
771  }
772  }
773  }
774 
775  return true;
776 }
777 
778 
780 {
781  int count = 0;
782  wxString netname;
783  wxString msg;
784  PAD* previouspad = nullptr;
785 
786  // We need the pad list for next tests.
787 
789 
790  std::vector<PAD*> padlist = m_board->GetPads();
791 
792  // Sort pads by netlist name
793  std::sort( padlist.begin(), padlist.end(),
794  [ this ]( PAD* a, PAD* b ) -> bool
795  {
796  return getNetname( a ) < getNetname( b );
797  } );
798 
799  for( PAD* pad : padlist )
800  {
801  if( getNetname( pad ).IsEmpty() )
802  continue;
803 
804  if( netname != getNetname( pad ) ) // End of net
805  {
806  if( previouspad && count == 1 )
807  {
808  // First, see if we have a copper zone attached to this pad.
809  // If so, this is not really a single pad net
810 
811  for( ZONE* zone : m_board->Zones() )
812  {
813  if( !zone->IsOnCopperLayer() )
814  continue;
815 
816  if( zone->GetIsRuleArea() )
817  continue;
818 
819  if( zone->GetNetname() == getNetname( previouspad ) )
820  {
821  count++;
822  break;
823  }
824  }
825 
826  if( count == 1 ) // Really one pad, and nothing else
827  {
828  if( m_isDryRun )
829  {
830  cacheNetname( previouspad, wxEmptyString );
831  msg.Printf( _( "Remove single pad net %s." ),
832  UnescapeString( getNetname( previouspad ) ) );
833  }
834  else
835  {
836  previouspad->SetNetCode( NETINFO_LIST::UNCONNECTED );
837  msg.Printf( _( "Removed single pad net %s." ),
838  UnescapeString( getNetname( previouspad ) ) );
839  }
840 
842  }
843  }
844 
845  netname = getNetname( pad );
846  count = 1;
847  }
848  else
849  {
850  count++;
851  }
852 
853  previouspad = pad;
854  }
855 
856  // Examine last pad
857  if( count == 1 )
858  {
859  if( !m_isDryRun )
860  previouspad->SetNetCode( NETINFO_LIST::UNCONNECTED );
861  else
862  cacheNetname( previouspad, wxEmptyString );
863  }
864 
865  return true;
866 }
867 
868 
870  std::map<COMPONENT*, FOOTPRINT*>& aFootprintMap )
871 {
872  // Verify that board contains all pads in netlist: if it doesn't then footprints are
873  // wrong or missing.
874 
875  wxString msg;
876  wxString padname;
877 
878  for( int i = 0; i < (int) aNetlist.GetCount(); i++ )
879  {
880  COMPONENT* component = aNetlist.GetComponent( i );
881  FOOTPRINT* footprint = aFootprintMap[component];
882 
883  if( !footprint ) // It can be missing in partial designs
884  continue;
885 
886  // Explore all pins/pads in component
887  for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
888  {
889  const COMPONENT_NET& net = component->GetNet( jj );
890  padname = net.GetPinName();
891 
892  if( footprint->FindPadByName( padname ) )
893  continue; // OK, pad found
894 
895  // not found: bad footprint, report error
896  msg.Printf( _( "%s pad %s not found in %s." ),
897  component->GetReference(),
898  padname,
899  footprint->GetFPID().Format().wx_str() );
901  ++m_errorCount;
902  }
903  }
904 
905  return true;
906 }
907 
908 
910 {
911  FOOTPRINT* lastPreexistingFootprint = nullptr;
912  COMPONENT* component = nullptr;
913  wxString msg;
914 
915  m_errorCount = 0;
916  m_warningCount = 0;
918 
919  std::map<COMPONENT*, FOOTPRINT*> footprintMap;
920 
921  if( !m_board->Footprints().empty() )
922  lastPreexistingFootprint = m_board->Footprints().back();
923 
925 
926  // First mark all nets (except <no net>) as stale; we'll update those which are current
927  // in the following two loops.
928  //
929  if( !m_isDryRun )
930  {
931  m_board->SetStatus( 0 );
932 
933  for( NETINFO_ITEM* net : m_board->GetNetInfo() )
934  net->SetIsCurrent( net->GetNetCode() == 0 );
935  }
936 
937  // Next go through the netlist updating all board footprints which have matching component
938  // entries and adding new footprints for those that don't.
939  //
940  for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
941  {
942  component = aNetlist.GetComponent( i );
943 
944  if( component->GetProperties().count( "exclude_from_board" ) )
945  continue;
946 
947  msg.Printf( _( "Processing symbol '%s:%s'." ),
948  component->GetReference(),
949  component->GetFPID().Format().wx_str() );
951 
952  int matchCount = 0;
953 
954  for( FOOTPRINT* footprint : m_board->Footprints() )
955  {
956  bool match = false;
957 
958  if( m_lookupByTimestamp )
959  {
960  for( const KIID& uuid : component->GetKIIDs() )
961  {
962  KIID_PATH base = component->GetPath();
963  base.push_back( uuid );
964 
965  if( footprint->GetPath() == base )
966  {
967  match = true;
968  break;
969  }
970  }
971  }
972  else
973  {
974  match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
975  }
976 
977  if( match )
978  {
979  FOOTPRINT* tmp = footprint;
980 
981  if( m_replaceFootprints && component->GetFPID() != footprint->GetFPID() )
982  tmp = replaceFootprint( aNetlist, footprint, component );
983 
984  if( tmp )
985  {
986  footprintMap[ component ] = tmp;
987 
988  updateFootprintParameters( tmp, component );
989  updateComponentPadConnections( tmp, component );
990  }
991 
992  matchCount++;
993  }
994 
995  if( footprint == lastPreexistingFootprint )
996  {
997  // No sense going through the newly-created footprints: end of loop
998  break;
999  }
1000  }
1001 
1002  if( matchCount == 0 )
1003  {
1004  FOOTPRINT* footprint = addNewFootprint( component );
1005 
1006  if( footprint )
1007  {
1008  footprintMap[ component ] = footprint;
1009 
1010  updateFootprintParameters( footprint, component );
1011  updateComponentPadConnections( footprint, component );
1012  }
1013  }
1014  else if( matchCount > 1 )
1015  {
1016  msg.Printf( _( "Multiple footprints found for '%s'." ),
1017  component->GetReference() );
1019  }
1020  }
1021 
1022  updateCopperZoneNets( aNetlist );
1023 
1024  // Finally go through the board footprints and update all those that *don't* have matching
1025  // component entries.
1026  //
1027  for( FOOTPRINT* footprint : m_board->Footprints() )
1028  {
1029  bool matched = false;
1030  bool doDelete = m_deleteUnusedFootprints;
1031 
1032  if( ( footprint->GetAttributes() & FP_BOARD_ONLY ) > 0 )
1033  doDelete = false;
1034 
1035  if( m_lookupByTimestamp )
1036  component = aNetlist.GetComponentByPath( footprint->GetPath() );
1037  else
1038  component = aNetlist.GetComponentByReference( footprint->GetReference() );
1039 
1040  if( component && component->GetProperties().count( "exclude_from_board" ) == 0 )
1041  matched = true;
1042 
1043  if( doDelete && !matched && footprint->IsLocked() )
1044  {
1045  if( m_isDryRun )
1046  {
1047  msg.Printf( _( "Cannot remove unused footprint %s (locked)." ),
1048  footprint->GetReference() );
1049  }
1050  else
1051  {
1052  msg.Printf( _( "Could not remove unused footprint %s (locked)." ),
1053  footprint->GetReference() );
1054  }
1055 
1057  doDelete = false;
1058  }
1059 
1060  if( doDelete && !matched )
1061  {
1062  if( m_isDryRun )
1063  {
1064  msg.Printf( _( "Remove unused footprint %s." ), footprint->GetReference() );
1065  }
1066  else
1067  {
1068  m_commit.Remove( footprint );
1069  msg.Printf( _( "Removed unused footprint %s." ), footprint->GetReference() );
1070  }
1071 
1073  }
1074  else if( !m_isDryRun )
1075  {
1076  if( !matched )
1077  footprint->SetPath( KIID_PATH() );
1078 
1079  for( PAD* pad : footprint->Pads() )
1080  {
1081  if( pad->GetNet() )
1082  pad->GetNet()->SetIsCurrent( true );
1083  }
1084  }
1085  }
1086 
1087  if( !m_isDryRun )
1088  {
1089  m_board->GetConnectivity()->Build( m_board );
1090  testConnectivity( aNetlist, footprintMap );
1091 
1092  // Now the connectivity data is rebuilt, we can delete single pads nets
1093  if( m_deleteSinglePadNets )
1095 
1096  for( NETINFO_ITEM* net : m_board->GetNetInfo() )
1097  {
1098  if( !net->IsCurrent() )
1099  {
1100  msg.Printf( _( "Removed unused net %s." ), net->GetNetname() );
1102  m_commit.Removed( net );
1103  }
1104  }
1105 
1108  m_commit.Push( _( "Update netlist" ) );
1109 
1112  }
1114  {
1115  // We can delete single net pads in dry run mode only if no new footprints
1116  // are added, because these new footprints are not actually added to the board
1117  // and the current pad list is wrong in this case.
1119  }
1120 
1121  if( m_isDryRun )
1122  {
1123  for( const std::pair<const wxString, NETINFO_ITEM*>& addedNet : m_addedNets )
1124  delete addedNet.second;
1125 
1126  m_addedNets.clear();
1127  }
1128 
1129  // Update the ratsnest
1130  m_reporter->ReportTail( wxT( "" ), RPT_SEVERITY_ACTION );
1131  m_reporter->ReportTail( wxT( "" ), RPT_SEVERITY_ACTION );
1132 
1133  msg.Printf( _( "Total warnings: %d, errors: %d." ), m_warningCount, m_errorCount );
1135 
1136  return true;
1137 }
void SetReference(const wxString &aReference)
Definition: footprint.h:430
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1343
void BuildListOfNets()
Definition: board.h:663
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:360
ZONES & Zones()
Definition: board.h:239
const wxString & GetValue() const
Definition: footprint.h:443
const KIID_PATH & GetPath() const
Definition: footprint.h:194
wxString getNetname(PAD *aPad)
void SetPath(const KIID_PATH &aPath)
Definition: footprint.h:195
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:742
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:93
int GetWidth() const
Definition: eda_rect.h:109
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
BOARD_NETLIST_UPDATER class definition.
unsigned GetCount() const
Definition: pcb_netlist.h:228
int GetStatus(EDA_ITEM *aItem)
Definition: commit.cpp:132
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:684
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:227
PADS & Pads()
Definition: footprint.h:159
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:1409
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,...
PCB_LAYER_ID
A quick note on layer IDs:
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
const std::vector< PAD * > GetPads() const
Return a reference to a list of all the pads.
Definition: board.cpp:1913
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
const wxString & GetReference() const
Definition: footprint.h:421
#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:185
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
COMMIT & Remove(EDA_ITEM *aItem)
Notify observers that aItem has been removed.
Definition: commit.h:90
PAD * FindPadByName(const wxString &aPadName) const
Return a PAD with a matching name.
Definition: footprint.cpp:971
void SetValue(const wxString &aValue)
Definition: footprint.h:451
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
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:226
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
FOOTPRINT * addNewFootprint(COMPONENT *aComponent)
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:222
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:464
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:465
Definition: pad.h:57
void SetPosition(const wxPoint &aPos) override
Definition: footprint.cpp:1452
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:1183
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:136
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:62
const std::vector< KIID > & GetKIIDs() const
Definition: pcb_netlist.h:140