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@verizon.net>
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 <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_addedComponents.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  aPcbComponent->GetReference(),
228  aPcbComponent->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( aPcbComponent, newFootprint, m_commit );
237 
238  msg.Printf( _( "Changed %s footprint from '%s' to '%s'."),
239  aPcbComponent->GetReference(),
240  aPcbComponent->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  cachePinFunction( pad, pinFunction );
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->GetName() );
453  }
454  else
455  {
456  msg.Printf( _( "Disconnected %s pin %s." ),
457  aFootprint->GetReference(),
458  pad->GetName() );
459  }
460 
462  }
463  else if( m_warnForNoNetPads && pad->IsOnCopperLayer() && !pad->GetName().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->GetName() );
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  cacheNetname( pad, wxEmptyString );
485  }
486  else // New footprint pad has a net.
487  {
488  const wxString& netName = net.GetNetName();
489  NETINFO_ITEM* netinfo = m_board->FindNet( netName );
490 
491  if( netinfo && !m_isDryRun )
492  netinfo->SetIsCurrent( true );
493 
494  if( pad->GetNetname() != netName )
495  {
496 
497  if( netinfo == nullptr )
498  {
499  // It might be a new net that has not been added to the board yet
500  if( m_addedNets.count( netName ) )
501  netinfo = m_addedNets[ netName ];
502  }
503 
504  if( netinfo == nullptr )
505  {
506  netinfo = new NETINFO_ITEM( m_board, netName );
507 
508  // It is a new net, we have to add it
509  if( !m_isDryRun )
510  {
511  changed = true;
512  m_commit.Add( netinfo );
513  }
514 
515  m_addedNets[netName] = netinfo;
516  msg.Printf( _( "Add net %s." ), UnescapeString( netName ) );
518  }
519 
520  if( !pad->GetNetname().IsEmpty() )
521  {
522  m_oldToNewNets[ pad->GetNetname() ] = netName;
523 
524  if( m_isDryRun )
525  {
526  msg.Printf( _( "Reconnect %s pin %s from %s to %s."),
527  aFootprint->GetReference(),
528  pad->GetName(),
529  UnescapeString( pad->GetNetname() ),
530  UnescapeString( netName ) );
531  }
532  else
533  {
534  msg.Printf( _( "Reconnected %s pin %s from %s to %s."),
535  aFootprint->GetReference(),
536  pad->GetName(),
537  UnescapeString( pad->GetNetname() ),
538  UnescapeString( netName ) );
539  }
540  }
541  else
542  {
543  if( m_isDryRun )
544  {
545  msg.Printf( _( "Connect %s pin %s to %s."),
546  aFootprint->GetReference(),
547  pad->GetName(),
548  UnescapeString( netName ) );
549  }
550  else
551  {
552  msg.Printf( _( "Connected %s pin %s to %s."),
553 
554  aFootprint->GetReference(),
555  pad->GetName(),
556  UnescapeString( netName ) );
557  }
558  }
559 
561 
562  if( !m_isDryRun )
563  {
564  changed = true;
565  pad->SetNet( netinfo );
566  }
567  else
568  {
569  cacheNetname( pad, netName );
570  }
571  }
572  }
573  }
574 
575  if( changed && copy )
576  m_commit.Modified( aFootprint, copy );
577  else
578  delete copy;
579 
580  return true;
581 }
582 
583 
585 {
586  for( ZONE* zone : m_board->Zones() )
587  {
588  if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
589  continue;
590 
591  m_zoneConnectionsCache[ zone ] = m_board->GetConnectivity()->GetConnectedPads( zone );
592  }
593 }
594 
595 
597 {
598  wxString msg;
599  std::set<wxString> netlistNetnames;
600 
601  for( int ii = 0; ii < (int) aNetlist.GetCount(); ii++ )
602  {
603  const COMPONENT* component = aNetlist.GetComponent( ii );
604  for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
605  {
606  const COMPONENT_NET& net = component->GetNet( jj );
607  netlistNetnames.insert( net.GetNetName() );
608  }
609  }
610 
611  for( TRACK* via : m_board->Tracks() )
612  {
613  if( via->Type() != PCB_VIA_T )
614  continue;
615 
616  if( netlistNetnames.count( via->GetNetname() ) == 0 )
617  {
618  wxString updatedNetname = wxEmptyString;
619 
620  // Take via name from name change map if it didn't match to a new pad
621  // (this is useful for stitching vias that don't connect to tracks)
622  if( m_oldToNewNets.count( via->GetNetname() ) )
623  {
624  updatedNetname = m_oldToNewNets[via->GetNetname()];
625  }
626 
627  if( !updatedNetname.IsEmpty() )
628  {
629  if( m_isDryRun )
630  {
631  msg.Printf( _( "Reconnect via from %s to %s." ),
632  UnescapeString( via->GetNetname() ),
633  UnescapeString( updatedNetname ) );
634 
636  }
637  else
638  {
639  NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
640 
641  if( !netinfo )
642  netinfo = m_addedNets[updatedNetname];
643 
644  if( netinfo )
645  {
646  m_commit.Modify( via );
647  via->SetNet( netinfo );
648 
649  msg.Printf( _( "Reconnected via from %s to %s." ),
650  UnescapeString( via->GetNetname() ),
651  UnescapeString( updatedNetname ) );
652 
654  }
655  }
656  }
657  else
658  {
659  msg.Printf( _( "Via connected to unknown net (%s)." ),
660  UnescapeString( via->GetNetname() ) );
662  ++m_warningCount;
663  }
664  }
665  }
666 
667  // Test copper zones to detect "dead" nets (nets without any pad):
668  for( ZONE* zone : m_board->Zones() )
669  {
670  if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
671  continue;
672 
673  if( netlistNetnames.count( zone->GetNetname() ) == 0 )
674  {
675  // Look for a pad in the zone's connected-pad-cache which has been updated to
676  // a new net and use that. While this won't always be the right net, the dead
677  // net is guaranteed to be wrong.
678  wxString updatedNetname = wxEmptyString;
679 
680  for( PAD* pad : m_zoneConnectionsCache[ zone ] )
681  {
682  if( getNetname( pad ) != zone->GetNetname() )
683  {
684  updatedNetname = getNetname( pad );
685  break;
686  }
687  }
688 
689  // Take zone name from name change map if it didn't match to a new pad
690  // (this is useful for zones on internal layers)
691  if( updatedNetname.IsEmpty() && m_oldToNewNets.count( zone->GetNetname() ) )
692  {
693  updatedNetname = m_oldToNewNets[ zone->GetNetname() ];
694  }
695 
696  if( !updatedNetname.IsEmpty() )
697  {
698  if( m_isDryRun )
699  {
700  if( !zone->GetZoneName().IsEmpty() )
701  {
702  msg.Printf( _( "Reconnect copper zone '%s' from %s to %s." ),
703  zone->GetZoneName(),
704  UnescapeString( zone->GetNetname() ),
705  UnescapeString( updatedNetname ) );
706  }
707  else
708  {
709  msg.Printf( _( "Reconnect copper zone from %s to %s." ),
710  UnescapeString( zone->GetNetname() ),
711  UnescapeString( updatedNetname ) );
712  }
713 
715  }
716  else
717  {
718  NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
719 
720  if( !netinfo )
721  netinfo = m_addedNets[ updatedNetname ];
722 
723  if( netinfo )
724  {
725  m_commit.Modify( zone );
726  zone->SetNet( netinfo );
727 
728  if( !zone->GetZoneName().IsEmpty() )
729  {
730  msg.Printf( _( "Reconnected copper zone '%s' from %s to %s." ),
731  zone->GetZoneName(),
732  UnescapeString( zone->GetNetname() ),
733  UnescapeString( updatedNetname ) );
734  }
735  else
736  {
737  msg.Printf( _( "Reconnected copper zone from %s to %s." ),
738  UnescapeString( zone->GetNetname() ),
739  UnescapeString( updatedNetname ) );
740  }
741 
743  }
744  }
745  }
746  else
747  {
748  if( !zone->GetZoneName().IsEmpty() )
749  {
750  msg.Printf( _( "Copper zone '%s' has no pads connected." ),
751  zone->GetZoneName() );
752  }
753  else
754  {
755  PCB_LAYER_ID layer = zone->GetLayer();
756  wxPoint pos = zone->GetPosition();
757 
758  msg.Printf( _( "Copper zone on layer %s at (%s, %s) has no pads connected." ),
759  m_board->GetLayerName( layer ),
762  }
763 
765  ++m_warningCount;
766  }
767  }
768  }
769 
770  return true;
771 }
772 
773 
775 {
776  int count = 0;
777  wxString netname;
778  wxString msg;
779  PAD* previouspad = NULL;
780 
781  // We need the pad list for next tests.
782 
784 
785  std::vector<PAD*> padlist = m_board->GetPads();
786 
787  // Sort pads by netlist name
788  std::sort( padlist.begin(), padlist.end(),
789  [ this ]( PAD* a, PAD* b ) -> bool
790  {
791  return getNetname( a ) < getNetname( b );
792  } );
793 
794  for( PAD* pad : padlist )
795  {
796  if( getNetname( pad ).IsEmpty() )
797  continue;
798 
799  if( netname != getNetname( pad ) ) // End of net
800  {
801  if( previouspad && count == 1 )
802  {
803  // First, see if we have a copper zone attached to this pad.
804  // If so, this is not really a single pad net
805 
806  for( ZONE* zone : m_board->Zones() )
807  {
808  if( !zone->IsOnCopperLayer() )
809  continue;
810 
811  if( zone->GetIsRuleArea() )
812  continue;
813 
814  if( zone->GetNetname() == getNetname( previouspad ) )
815  {
816  count++;
817  break;
818  }
819  }
820 
821  if( count == 1 ) // Really one pad, and nothing else
822  {
823  if( m_isDryRun )
824  {
825  cacheNetname( previouspad, wxEmptyString );
826  msg.Printf( _( "Remove single pad net %s." ),
827  UnescapeString( getNetname( previouspad ) ) );
828  }
829  else
830  {
831  previouspad->SetNetCode( NETINFO_LIST::UNCONNECTED );
832  msg.Printf( _( "Removed single pad net %s." ),
833  UnescapeString( getNetname( previouspad ) ) );
834  }
835 
837  }
838  }
839 
840  netname = getNetname( pad );
841  count = 1;
842  }
843  else
844  {
845  count++;
846  }
847 
848  previouspad = pad;
849  }
850 
851  // Examine last pad
852  if( count == 1 )
853  {
854  if( !m_isDryRun )
855  previouspad->SetNetCode( NETINFO_LIST::UNCONNECTED );
856  else
857  cacheNetname( previouspad, wxEmptyString );
858  }
859 
860  return true;
861 }
862 
863 
865  std::map<COMPONENT*, FOOTPRINT*>& aFootprintMap )
866 {
867  // Verify that board contains all pads in netlist: if it doesn't then footprints are
868  // wrong or missing.
869 
870  wxString msg;
871  wxString padname;
872 
873  for( int i = 0; i < (int) aNetlist.GetCount(); i++ )
874  {
875  COMPONENT* component = aNetlist.GetComponent( i );
876  FOOTPRINT* footprint = aFootprintMap[component];
877 
878  if( !footprint ) // It can be missing in partial designs
879  continue;
880 
881  // Explore all pins/pads in component
882  for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
883  {
884  const COMPONENT_NET& net = component->GetNet( jj );
885  padname = net.GetPinName();
886 
887  if( footprint->FindPadByName( padname ) )
888  continue; // OK, pad found
889 
890  // not found: bad footprint, report error
891  msg.Printf( _( "%s pad %s not found in %s." ),
892  component->GetReference(),
893  padname,
894  footprint->GetFPID().Format().wx_str() );
896  ++m_errorCount;
897  }
898  }
899 
900  return true;
901 }
902 
903 
905 {
906  FOOTPRINT* lastPreexistingFootprint = nullptr;
907  COMPONENT* component = nullptr;
908  wxString msg;
909 
910  m_errorCount = 0;
911  m_warningCount = 0;
913 
914  std::map<COMPONENT*, FOOTPRINT*> footprintMap;
915 
916  if( !m_board->Footprints().empty() )
917  lastPreexistingFootprint = m_board->Footprints().back();
918 
920 
921  // First mark all nets (except <no net>) as stale; we'll update those which are current
922  // in the following two loops.
923  //
924  if( !m_isDryRun )
925  {
926  m_board->SetStatus( 0 );
927 
928  for( NETINFO_ITEM* net : m_board->GetNetInfo() )
929  net->SetIsCurrent( net->GetNetCode() == 0 );
930  }
931 
932  // Next go through the netlist updating all board footprints which have matching component
933  // entries and adding new footprints for those that don't.
934  //
935  for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
936  {
937  component = aNetlist.GetComponent( i );
938 
939  if( component->GetProperties().count( "exclude_from_board" ) )
940  continue;
941 
942  msg.Printf( _( "Processing symbol '%s:%s'." ),
943  component->GetReference(),
944  component->GetFPID().Format().wx_str() );
946 
947  int matchCount = 0;
948 
949  for( FOOTPRINT* footprint : m_board->Footprints() )
950  {
951  bool match = false;
952 
953  if( m_lookupByTimestamp )
954  {
955  for( auto& uuid : component->GetKIIDs() )
956  {
957  KIID_PATH base = component->GetPath();
958  base.push_back( uuid );
959 
960  if( footprint->GetPath() == base )
961  {
962  match = true;
963  break;
964  }
965  }
966  }
967  else
968  match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
969 
970  if( match )
971  {
972  FOOTPRINT* tmp = footprint;
973 
974  if( m_replaceFootprints && component->GetFPID() != footprint->GetFPID() )
975  tmp = replaceComponent( aNetlist, footprint, component );
976 
977  if( tmp )
978  {
979  footprintMap[ component ] = tmp;
980 
981  updateFootprintParameters( tmp, component );
982  updateComponentPadConnections( tmp, component );
983  }
984 
985  matchCount++;
986  }
987 
988  if( footprint == lastPreexistingFootprint )
989  {
990  // No sense going through the newly-created footprints: end of loop
991  break;
992  }
993  }
994 
995  if( matchCount == 0 )
996  {
997  FOOTPRINT* footprint = addNewComponent( component );
998 
999  if( footprint )
1000  {
1001  footprintMap[ component ] = footprint;
1002 
1003  updateFootprintParameters( footprint, component );
1004  updateComponentPadConnections( footprint, component );
1005  }
1006  }
1007  else if( matchCount > 1 )
1008  {
1009  msg.Printf( _( "Multiple footprints found for \"%s\"." ),
1010  component->GetReference() );
1012  }
1013  }
1014 
1015  updateCopperZoneNets( aNetlist );
1016 
1017  // Finally go through the board footprints and update all those that *don't* have matching
1018  // component entries.
1019  //
1020  for( FOOTPRINT* footprint : m_board->Footprints() )
1021  {
1022  bool doDelete = m_deleteUnusedComponents;
1023 
1024  if( ( footprint->GetAttributes() & FP_BOARD_ONLY ) > 0 )
1025  doDelete = false;
1026 
1027  if( doDelete )
1028  {
1029  if( m_lookupByTimestamp )
1030  component = aNetlist.GetComponentByPath( footprint->GetPath() );
1031  else
1032  component = aNetlist.GetComponentByReference( footprint->GetReference() );
1033 
1034  if( component && component->GetProperties().count( "exclude_from_board" ) == 0 )
1035  doDelete = false;
1036  }
1037 
1038  if( doDelete && footprint->IsLocked() )
1039  {
1040  if( m_isDryRun )
1041  {
1042  msg.Printf( _( "Cannot remove unused footprint %s (locked)." ),
1043  footprint->GetReference() );
1044  }
1045  else
1046  {
1047  msg.Printf( _( "Could not remove unused footprint %s (locked)." ),
1048  footprint->GetReference() );
1049  }
1050 
1052  doDelete = false;
1053  }
1054 
1055  if( doDelete )
1056  {
1057  if( m_isDryRun )
1058  {
1059  msg.Printf( _( "Remove unused footprint %s." ), footprint->GetReference() );
1060  }
1061  else
1062  {
1063  m_commit.Remove( footprint );
1064  msg.Printf( _( "Removed unused footprint %s." ), footprint->GetReference() );
1065  }
1066 
1068  }
1069  else if( !m_isDryRun )
1070  {
1071  for( PAD* pad : footprint->Pads() )
1072  {
1073  if( pad->GetNet() )
1074  pad->GetNet()->SetIsCurrent( true );
1075  }
1076  }
1077  }
1078 
1079  if( !m_isDryRun )
1080  {
1081  m_board->GetConnectivity()->Build( m_board );
1082  testConnectivity( aNetlist, footprintMap );
1083 
1084  // Now the connectivity data is rebuilt, we can delete single pads nets
1085  if( m_deleteSinglePadNets )
1087 
1088  for( NETINFO_ITEM* net : m_board->GetNetInfo() )
1089  {
1090  if( !net->IsCurrent() )
1091  {
1092  msg.Printf( _( "Removed unused net %s." ), net->GetNetname() );
1094  m_commit.Removed( net );
1095  }
1096  }
1097 
1100  m_commit.Push( _( "Update netlist" ) );
1101 
1104  }
1106  {
1107  // We can delete single net pads in dry run mode only if no new footprints
1108  // are added, because these new footprints are not actually added to the board
1109  // and the current pad list is wrong in this case.
1111  }
1112 
1113  if( m_isDryRun )
1114  {
1115  for( const std::pair<const wxString, NETINFO_ITEM*>& addedNet : m_addedNets )
1116  delete addedNet.second;
1117 
1118  m_addedNets.clear();
1119  }
1120 
1121  // Update the ratsnest
1122  m_reporter->ReportTail( wxT( "" ), RPT_SEVERITY_ACTION );
1123  m_reporter->ReportTail( wxT( "" ), RPT_SEVERITY_ACTION );
1124 
1125  msg.Printf( _( "Total warnings: %d, errors: %d." ), m_warningCount, m_errorCount );
1127 
1128  return true;
1129 }
void SetReference(const wxString &aReference)
Definition: footprint.h:435
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1291
void BuildListOfNets()
Definition: board.h:735
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:90
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:608
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:342
ZONES & Zones()
Definition: board.h:311
const wxString & GetValue() const
Definition: footprint.h:448
const KIID_PATH & GetPath() const
Definition: footprint.h:199
wxString getNetname(PAD *aPad)
void SetPath(const KIID_PATH &aPath)
Definition: footprint.h:200
COMMIT & Add(EDA_ITEM *aItem)
Notify observers that aItem has been added.
Definition: commit.h:78
const EDA_RECT GetBoardEdgesBoundingBox() const
Returns the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:812
void SetIsCurrent(bool isCurrent)
Definition: netinfo.h:141
const wxString & GetPinType() const
Definition: pcb_netlist.h:65
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:114
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
wxString GetNetname() const
BOARD_NETLIST_UPDATER class definition.
unsigned GetCount() const
Definition: pcb_netlist.h:227
int GetStatus(EDA_ITEM *aItem)
Definition: commit.cpp:129
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:754
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:189
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:166
void SetAttributes(int aAttributes)
Definition: footprint.h:232
PADS & Pads()
Definition: footprint.h:164
int GetBottom() const
Definition: eda_rect.h:119
std::map< wxString, NETINFO_ITEM * > m_addedNets
const LIB_ID & GetFPID() const
Definition: pcb_netlist.h:132
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:1436
wxString getPinFunction(PAD *aPad)
FOOTPRINT * addNewComponent(COMPONENT *aComponent)
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:63
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
Definition: pcb_netlist.h:205
#define NULL
std::vector< FOOTPRINT * > m_addedComponents
std::map< ZONE *, std::vector< PAD * > > m_zoneConnectionsCache
unsigned GetNetCount() const
Definition: pcb_netlist.h:108
const std::vector< PAD * > GetPads() const
Return a reference to a list of all the pads.
Definition: board.cpp:1940
FOOTPRINTS & Footprints()
Definition: board.h:305
void SaveProjectSettings() override
Save changes to the project settings to the project (.pro) file.
const wxString & GetReference() const
Definition: pcb_netlist.h:122
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:416
const wxString & GetReference() const
Definition: footprint.h:426
FOOTPRINT * replaceComponent(NETLIST &aNetlist, FOOTPRINT *aPcbComponent, 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:190
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:957
void SetValue(const wxString &aValue)
Definition: footprint.h:456
const COMPONENT_NET & GetNet(unsigned aIndex) const
Definition: pcb_netlist.h:110
int GetHeight() const
Definition: eda_rect.h:115
UTF8 Format() const
Definition: lib_id.cpp:233
Store all of the related footprint information found in a netlist.
Definition: pcb_netlist.h:83
const wxString & GetPinFunction() const
Definition: pcb_netlist.h:64
bool IsValid() const
Definition: pcb_netlist.h:67
COMPONENT * GetComponent(unsigned aIndex)
Returns the COMPONENT at aIndex.
Definition: pcb_netlist.h:235
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:231
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define _(s)
Definition: 3d_actions.cpp:33
wxString UnescapeString(const wxString &aSource)
Definition: string.cpp:151
wxString AsString() const
Definition: kiid.cpp:277
wxString wx_str() const
Definition: utf8.cpp:51
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.
void SetStatus(STATUS_FLAGS aStatus)
Definition: eda_item.h:201
wxPoint Centre() const
Definition: eda_rect.h:60
const wxString & GetPinName() const
Definition: pcb_netlist.h:62
const std::map< wxString, wxString > & GetProperties() const
Definition: footprint.h:469
std::map< PAD *, wxString > m_padNets
const std::map< wxString, wxString > & GetProperties() const
Definition: pcb_netlist.h:129
const wxString & GetValue() const
Definition: pcb_netlist.h:123
COMPONENT * GetComponentByReference(const wxString &aReference)
Return a COMPONENT by aReference.
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
static REPORTER & GetInstance()
Definition: reporter.cpp:105
void SetProperties(const std::map< wxString, wxString > &aProps)
Definition: footprint.h:470
Definition: pad.h:60
void SetPosition(const wxPoint &aPos) override
Definition: footprint.cpp:1438
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:1169
void cacheNetname(PAD *aPad, const wxString &aNetname)
const KIID_PATH & GetPath() const
Definition: pcb_netlist.h:137
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:139
bool IsEmpty() const
Definition: board.h:353
TRACKS & Tracks()
Definition: board.h:302
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
Definition: track.h:83
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:139