KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2023 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 <base_units.h>
33#include <board.h>
34#include <netinfo.h>
35#include <footprint.h>
36#include <pad.h>
37#include <pcb_track.h>
38#include <zone.h>
39#include <string_utils.h>
40#include <pcbnew_settings.h>
41#include <pcb_edit_frame.h>
44#include <reporter.h>
45
47
48
50 m_frame( aFrame ),
51 m_commit( aFrame ),
52 m_board( aBoard )
53{
55
57 m_isDryRun = false;
59 m_lookupByTimestamp = false;
60
62 m_errorCount = 0;
64}
65
66
68{
69}
70
71
72// These functions allow inspection of pad nets during dry runs by keeping a cache of
73// current pad netnames indexed by pad.
74
75void BOARD_NETLIST_UPDATER::cacheNetname( PAD* aPad, const wxString& aNetname )
76{
77 m_padNets[ aPad ] = aNetname;
78}
79
80
82{
83 if( m_isDryRun && m_padNets.count( aPad ) )
84 return m_padNets[ aPad ];
85 else
86 return aPad->GetNetname();
87}
88
89
90void BOARD_NETLIST_UPDATER::cachePinFunction( PAD* aPad, const wxString& aPinFunction )
91{
92 m_padPinFunctions[ aPad ] = aPinFunction;
93}
94
95
97{
98 if( m_isDryRun && m_padPinFunctions.count( aPad ) )
99 return m_padPinFunctions[ aPad ];
100 else
101 return aPad->GetPinFunction();
102}
103
104
106{
107 VECTOR2I bestPosition;
108
109 if( !m_board->IsEmpty() )
110 {
111 // Position new components below any existing board features.
113
114 if( bbox.GetWidth() || bbox.GetHeight() )
115 {
116 bestPosition.x = bbox.Centre().x;
117 bestPosition.y = bbox.GetBottom() + pcbIUScale.mmToIU( 10 );
118 }
119 }
120 else
121 {
122 // Position new components in the center of the page when the board is empty.
124
125 bestPosition.x = pageSize.x / 2;
126 bestPosition.y = pageSize.y / 2;
127 }
128
129 return bestPosition;
130}
131
132
134{
135 wxString msg;
136
137 if( aComponent->GetFPID().empty() )
138 {
139 msg.Printf( _( "Cannot add %s (no footprint assigned)." ),
140 aComponent->GetReference(),
141 aComponent->GetFPID().Format().wx_str() );
143 ++m_errorCount;
144 return nullptr;
145 }
146
147 FOOTPRINT* footprint = m_frame->LoadFootprint( aComponent->GetFPID() );
148
149 if( footprint == nullptr )
150 {
151 msg.Printf( _( "Cannot add %s (footprint '%s' not found)." ),
152 aComponent->GetReference(),
153 aComponent->GetFPID().Format().wx_str() );
155 ++m_errorCount;
156 return nullptr;
157 }
158
159 if( m_isDryRun )
160 {
161 msg.Printf( _( "Add %s (footprint '%s')." ),
162 aComponent->GetReference(),
163 aComponent->GetFPID().Format().wx_str() );
164
165 delete footprint;
166 footprint = nullptr;
167 }
168 else
169 {
170 for( PAD* pad : footprint->Pads() )
171 {
172 // Set the pads ratsnest settings to the global settings
173 pad->SetLocalRatsnestVisible( m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest );
174
175 // Pads in the library all have orphaned nets. Replace with Default.
176 pad->SetNetCode( 0 );
177 }
178
179 footprint->SetParent( m_board );
181
182 // This flag is used to prevent connectivity from considering the footprint during its
183 // initial build after the footprint is committed, because we're going to immediately start
184 // a move operation on the footprint and don't want its pads to drive nets onto vias/tracks
185 // it happens to land on at the initial position.
186 footprint->SetAttributes( footprint->GetAttributes() | FP_JUST_ADDED );
187
188 m_addedFootprints.push_back( footprint );
189 m_commit.Add( footprint );
190
191 msg.Printf( _( "Added %s (footprint '%s')." ),
192 aComponent->GetReference(),
193 aComponent->GetFPID().Format().wx_str() );
194 }
195
198 return footprint;
199}
200
201
203 COMPONENT* aNewComponent )
204{
205 wxString msg;
206
207 if( aNewComponent->GetFPID().empty() )
208 {
209 msg.Printf( _( "Cannot update %s (no footprint assigned)." ),
210 aNewComponent->GetReference(),
211 aNewComponent->GetFPID().Format().wx_str() );
213 ++m_errorCount;
214 return nullptr;
215 }
216
217 FOOTPRINT* newFootprint = m_frame->LoadFootprint( aNewComponent->GetFPID() );
218
219 if( newFootprint == nullptr )
220 {
221 msg.Printf( _( "Cannot update %s (footprint '%s' not found)." ),
222 aNewComponent->GetReference(),
223 aNewComponent->GetFPID().Format().wx_str() );
225 ++m_errorCount;
226 return nullptr;
227 }
228
229 if( m_isDryRun )
230 {
231 msg.Printf( _( "Change %s footprint from '%s' to '%s'."),
232 aFootprint->GetReference(),
233 aFootprint->GetFPID().Format().wx_str(),
234 aNewComponent->GetFPID().Format().wx_str() );
235
236 delete newFootprint;
237 newFootprint = nullptr;
238 }
239 else
240 {
241 m_frame->ExchangeFootprint( aFootprint, newFootprint, m_commit );
242
243 msg.Printf( _( "Changed %s footprint from '%s' to '%s'."),
244 aFootprint->GetReference(),
245 aFootprint->GetFPID().Format().wx_str(),
246 aNewComponent->GetFPID().Format().wx_str() );
247 }
248
251 return newFootprint;
252}
253
254
256 COMPONENT* aNetlistComponent )
257{
258 wxString msg;
259
260 // Create a copy only if the footprint has not been added during this update
261 FOOTPRINT* copy = nullptr;
262
263 if( !m_commit.GetStatus( aPcbFootprint ) )
264 {
265 copy = static_cast<FOOTPRINT*>( aPcbFootprint->Clone() );
266 copy->SetParentGroup( nullptr );
267 }
268
269 bool changed = false;
270
271 // Test for reference designator field change.
272 if( aPcbFootprint->GetReference() != aNetlistComponent->GetReference() )
273 {
274 if( m_isDryRun )
275 {
276 msg.Printf( _( "Change %s reference designator to %s." ),
277 aPcbFootprint->GetReference(),
278 aNetlistComponent->GetReference() );
279 }
280 else
281 {
282 msg.Printf( _( "Changed %s reference designator to %s." ),
283 aPcbFootprint->GetReference(),
284 aNetlistComponent->GetReference() );
285
286 changed = true;
287 aPcbFootprint->SetReference( aNetlistComponent->GetReference() );
288 }
289
291 }
292
293 // Test for value field change.
294 if( aPcbFootprint->GetValue() != aNetlistComponent->GetValue() )
295 {
296 if( m_isDryRun )
297 {
298 msg.Printf( _( "Change %s value from %s to %s." ),
299 aPcbFootprint->GetReference(),
300 aPcbFootprint->GetValue(),
301 aNetlistComponent->GetValue() );
302 }
303 else
304 {
305 msg.Printf( _( "Changed %s value from %s to %s." ),
306 aPcbFootprint->GetReference(),
307 aPcbFootprint->GetValue(),
308 aNetlistComponent->GetValue() );
309
310 changed = true;
311 aPcbFootprint->SetValue( aNetlistComponent->GetValue() );
312 }
313
315 }
316
317 // Test for time stamp change.
318 KIID_PATH new_path = aNetlistComponent->GetPath();
319
320 if( !aNetlistComponent->GetKIIDs().empty() )
321 new_path.push_back( aNetlistComponent->GetKIIDs().front() );
322
323 if( aPcbFootprint->GetPath() != new_path )
324 {
325 if( m_isDryRun )
326 {
327 msg.Printf( _( "Update %s symbol association from %s to %s." ),
328 aPcbFootprint->GetReference(),
329 aPcbFootprint->GetPath().AsString(),
330 new_path.AsString() );
331 }
332 else
333 {
334 msg.Printf( _( "Updated %s symbol association from %s to %s." ),
335 aPcbFootprint->GetReference(),
336 aPcbFootprint->GetPath().AsString(),
337 new_path.AsString() );
338
339 changed = true;
340 aPcbFootprint->SetPath( new_path );
341 }
342
344 }
345
346 if( aPcbFootprint->GetProperties() != aNetlistComponent->GetProperties() )
347 {
348 if( m_isDryRun )
349 {
350 msg.Printf( _( "Update %s properties." ),
351 aPcbFootprint->GetReference() );
352 }
353 else
354 {
355 msg.Printf( _( "Updated %s properties." ),
356 aPcbFootprint->GetReference() );
357
358 changed = true;
359 aPcbFootprint->SetProperties( aNetlistComponent->GetProperties() );
360 }
361
363 }
364
365 if( ( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) > 0 )
366 != ( ( aPcbFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 ) )
367 {
368 if( m_isDryRun )
369 {
370 if( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) )
371 {
372 msg.Printf( _( "Add %s 'exclude from BOM' fabrication attribute." ),
373 aPcbFootprint->GetReference() );
374 }
375 else
376 {
377 msg.Printf( _( "Remove %s 'exclude from BOM' fabrication attribute." ),
378 aPcbFootprint->GetReference() );
379 }
380 }
381 else
382 {
383 int attributes = aPcbFootprint->GetAttributes();
384
385 if( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) )
386 {
387 attributes |= FP_EXCLUDE_FROM_BOM;
388 msg.Printf( _( "Added %s 'exclude from BOM' fabrication attribute." ),
389 aPcbFootprint->GetReference() );
390 }
391 else
392 {
393 attributes &= ~FP_EXCLUDE_FROM_BOM;
394 msg.Printf( _( "Removed %s 'exclude from BOM' fabrication attribute." ),
395 aPcbFootprint->GetReference() );
396 }
397
398 changed = true;
399 aPcbFootprint->SetAttributes( attributes );
400 }
401
403 }
404
405 if( ( aNetlistComponent->GetProperties().count( wxT( "dnp" ) ) > 0 )
406 != ( ( aPcbFootprint->GetAttributes() & FP_DNP ) > 0 ) )
407 {
408 if( m_isDryRun )
409 {
410 if( aNetlistComponent->GetProperties().count( wxT( "dnp" ) ) )
411 {
412 msg.Printf( _( "Add %s 'Do not place' fabrication attribute." ),
413 aPcbFootprint->GetReference() );
414 }
415 else
416 {
417 msg.Printf( _( "Remove %s 'Do not place' fabrication attribute." ),
418 aPcbFootprint->GetReference() );
419 }
420 }
421 else
422 {
423 int attributes = aPcbFootprint->GetAttributes();
424
425 if( aNetlistComponent->GetProperties().count( wxT( "dnp" ) ) )
426 {
427 attributes |= FP_DNP;
428 msg.Printf( _( "Added %s 'Do not place' fabrication attribute." ),
429 aPcbFootprint->GetReference() );
430 }
431 else
432 {
433 attributes &= ~FP_DNP;
434 msg.Printf( _( "Removed %s 'Do not place' fabrication attribute." ),
435 aPcbFootprint->GetReference() );
436 }
437
438 changed = true;
439 aPcbFootprint->SetAttributes( attributes );
440 }
441
443 }
444
445 if( changed && copy )
446 m_commit.Modified( aPcbFootprint, copy );
447 else if( copy )
448 delete copy;
449
450 return true;
451}
452
453
455 COMPONENT* aNewComponent )
456{
457 wxString msg;
458
459 // Create a copy only if the footprint has not been added during this update
460 FOOTPRINT* copy = nullptr;
461
462 if( !m_commit.GetStatus( aFootprint ) )
463 {
464 copy = static_cast<FOOTPRINT*>( aFootprint->Clone() );
465 copy->SetParentGroup( nullptr );
466 }
467
468 bool changed = false;
469
470 // At this point, the component footprint is updated. Now update the nets.
471 for( PAD* pad : aFootprint->Pads() )
472 {
473 const COMPONENT_NET& net = aNewComponent->GetNet( pad->GetNumber() );
474
475 wxString pinFunction;
476 wxString pinType;
477
478 if( net.IsValid() ) // i.e. the pad has a name
479 {
480 pinFunction = net.GetPinFunction();
481 pinType = net.GetPinType();
482 }
483
484 if( !m_isDryRun )
485 {
486 if( pad->GetPinFunction() != pinFunction )
487 {
488 changed = true;
489 pad->SetPinFunction( pinFunction );
490 }
491
492 if( pad->GetPinType() != pinType )
493 {
494 changed = true;
495 pad->SetPinType( pinType );
496 }
497 }
498 else
499 {
500 cachePinFunction( pad, pinFunction );
501 }
502
503 // Test if new footprint pad has no net (pads not on copper layers have no net).
504 if( !net.IsValid() || !pad->IsOnCopperLayer() )
505 {
506 if( !pad->GetNetname().IsEmpty() )
507 {
508 if( m_isDryRun )
509 {
510 msg.Printf( _( "Disconnect %s pin %s." ),
511 aFootprint->GetReference(),
512 pad->GetNumber() );
513 }
514 else
515 {
516 msg.Printf( _( "Disconnected %s pin %s." ),
517 aFootprint->GetReference(),
518 pad->GetNumber() );
519 }
520
522 }
523 else if( pad->IsOnCopperLayer() && !pad->GetNumber().IsEmpty() )
524 {
525 // pad is connectable but has no net found in netlist
526 msg.Printf( _( "No net found for component %s pad %s (no pin %s in symbol)." ),
527 pad->GetNumber(),
528 aFootprint->GetReference(),
529 pad->GetNumber() );
532 }
533
534 if( !m_isDryRun )
535 {
536 changed = true;
537 pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
538
539 // If the pad has no net from netlist (i.e. not in netlist
540 // it cannot have a pin function
541 if( pad->GetNetname().IsEmpty() )
542 pad->SetPinFunction( wxEmptyString );
543
544 }
545 else
546 {
547 cacheNetname( pad, wxEmptyString );
548 }
549 }
550 else // New footprint pad has a net.
551 {
552 const wxString& netName = net.GetNetName();
553 NETINFO_ITEM* netinfo = m_board->FindNet( netName );
554
555 if( netinfo && !m_isDryRun )
556 netinfo->SetIsCurrent( true );
557
558 if( pad->GetNetname() != netName )
559 {
560
561 if( netinfo == nullptr )
562 {
563 // It might be a new net that has not been added to the board yet
564 if( m_addedNets.count( netName ) )
565 netinfo = m_addedNets[ netName ];
566 }
567
568 if( netinfo == nullptr )
569 {
570 netinfo = new NETINFO_ITEM( m_board, netName );
571
572 // It is a new net, we have to add it
573 if( !m_isDryRun )
574 {
575 changed = true;
576 m_commit.Add( netinfo );
577 }
578
579 m_addedNets[netName] = netinfo;
580 msg.Printf( _( "Add net %s." ), UnescapeString( netName ) );
582 }
583
584 if( !pad->GetNetname().IsEmpty() )
585 {
586 m_oldToNewNets[ pad->GetNetname() ] = netName;
587
588 if( m_isDryRun )
589 {
590 msg.Printf( _( "Reconnect %s pin %s from %s to %s."),
591 aFootprint->GetReference(),
592 pad->GetNumber(),
593 UnescapeString( pad->GetNetname() ),
594 UnescapeString( netName ) );
595 }
596 else
597 {
598 msg.Printf( _( "Reconnected %s pin %s from %s to %s."),
599 aFootprint->GetReference(),
600 pad->GetNumber(),
601 UnescapeString( pad->GetNetname() ),
602 UnescapeString( netName ) );
603 }
604 }
605 else
606 {
607 if( m_isDryRun )
608 {
609 msg.Printf( _( "Connect %s pin %s to %s."),
610 aFootprint->GetReference(),
611 pad->GetNumber(),
612 UnescapeString( netName ) );
613 }
614 else
615 {
616 msg.Printf( _( "Connected %s pin %s to %s."),
617 aFootprint->GetReference(),
618 pad->GetNumber(),
619 UnescapeString( netName ) );
620 }
621 }
622
624
625 if( !m_isDryRun )
626 {
627 changed = true;
628 pad->SetNet( netinfo );
629 }
630 else
631 {
632 cacheNetname( pad, netName );
633 }
634 }
635 }
636 }
637
638 if( changed && copy )
639 m_commit.Modified( aFootprint, copy );
640 else if( copy )
641 delete copy;
642
643 return true;
644}
645
646
648{
649 for( ZONE* zone : m_board->Zones() )
650 {
651 if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
652 continue;
653
654 m_zoneConnectionsCache[ zone ] = m_board->GetConnectivity()->GetConnectedPads( zone );
655 }
656}
657
658
660{
661 wxString msg;
662 std::set<wxString> netlistNetnames;
663
664 for( int ii = 0; ii < (int) aNetlist.GetCount(); ii++ )
665 {
666 const COMPONENT* component = aNetlist.GetComponent( ii );
667
668 for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
669 {
670 const COMPONENT_NET& net = component->GetNet( jj );
671 netlistNetnames.insert( net.GetNetName() );
672 }
673 }
674
675 for( PCB_TRACK* via : m_board->Tracks() )
676 {
677 if( via->Type() != PCB_VIA_T )
678 continue;
679
680 if( netlistNetnames.count( via->GetNetname() ) == 0 )
681 {
682 wxString updatedNetname = wxEmptyString;
683
684 // Take via name from name change map if it didn't match to a new pad
685 // (this is useful for stitching vias that don't connect to tracks)
686 if( m_oldToNewNets.count( via->GetNetname() ) )
687 {
688 updatedNetname = m_oldToNewNets[via->GetNetname()];
689 }
690
691 if( !updatedNetname.IsEmpty() )
692 {
693 if( m_isDryRun )
694 {
695 wxString originalNetname = via->GetNetname();
696
697 msg.Printf( _( "Reconnect via from %s to %s." ),
698 UnescapeString( originalNetname ),
699 UnescapeString( updatedNetname ) );
700
702 }
703 else
704 {
705 NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
706
707 if( !netinfo )
708 netinfo = m_addedNets[updatedNetname];
709
710 if( netinfo )
711 {
712 wxString originalNetname = via->GetNetname();
713
715 via->SetNet( netinfo );
716
717 msg.Printf( _( "Reconnected via from %s to %s." ),
718 UnescapeString( originalNetname ),
719 UnescapeString( updatedNetname ) );
720
722 }
723 }
724 }
725 else
726 {
727 msg.Printf( _( "Via connected to unknown net (%s)." ),
728 UnescapeString( via->GetNetname() ) );
731 }
732 }
733 }
734
735 // Test copper zones to detect "dead" nets (nets without any pad):
736 for( ZONE* zone : m_board->Zones() )
737 {
738 if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
739 continue;
740
741 if( netlistNetnames.count( zone->GetNetname() ) == 0 )
742 {
743 // Look for a pad in the zone's connected-pad-cache which has been updated to
744 // a new net and use that. While this won't always be the right net, the dead
745 // net is guaranteed to be wrong.
746 wxString updatedNetname = wxEmptyString;
747
748 for( PAD* pad : m_zoneConnectionsCache[ zone ] )
749 {
750 if( getNetname( pad ) != zone->GetNetname() )
751 {
752 updatedNetname = getNetname( pad );
753 break;
754 }
755 }
756
757 // Take zone name from name change map if it didn't match to a new pad
758 // (this is useful for zones on internal layers)
759 if( updatedNetname.IsEmpty() && m_oldToNewNets.count( zone->GetNetname() ) )
760 {
761 updatedNetname = m_oldToNewNets[ zone->GetNetname() ];
762 }
763
764 if( !updatedNetname.IsEmpty() )
765 {
766 if( m_isDryRun )
767 {
768 wxString originalNetname = zone->GetNetname();
769
770 if( !zone->GetZoneName().IsEmpty() )
771 {
772 msg.Printf( _( "Reconnect copper zone '%s' from %s to %s." ),
773 zone->GetZoneName(),
774 UnescapeString( originalNetname ),
775 UnescapeString( updatedNetname ) );
776 }
777 else
778 {
779 msg.Printf( _( "Reconnect copper zone from %s to %s." ),
780 UnescapeString( originalNetname ),
781 UnescapeString( updatedNetname ) );
782 }
783
785 }
786 else
787 {
788 NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
789
790 if( !netinfo )
791 netinfo = m_addedNets[ updatedNetname ];
792
793 if( netinfo )
794 {
795 wxString originalNetname = zone->GetNetname();
796
797 m_commit.Modify( zone );
798 zone->SetNet( netinfo );
799
800 if( !zone->GetZoneName().IsEmpty() )
801 {
802 msg.Printf( _( "Reconnected copper zone '%s' from %s to %s." ),
803 zone->GetZoneName(),
804 UnescapeString( originalNetname ),
805 UnescapeString( updatedNetname ) );
806 }
807 else
808 {
809 msg.Printf( _( "Reconnected copper zone from %s to %s." ),
810 UnescapeString( originalNetname ),
811 UnescapeString( updatedNetname ) );
812 }
813
815 }
816 }
817 }
818 else
819 {
820 if( !zone->GetZoneName().IsEmpty() )
821 {
822 msg.Printf( _( "Copper zone '%s' has no pads connected." ),
823 zone->GetZoneName() );
824 }
825 else
826 {
827 PCB_LAYER_ID layer = zone->GetLayer();
828 VECTOR2I pos = zone->GetPosition();
829
830 msg.Printf( _( "Copper zone on layer %s at (%s, %s) has no pads connected." ),
831 m_board->GetLayerName( layer ),
834 }
835
838 }
839 }
840 }
841
842 return true;
843}
844
845
847 std::map<COMPONENT*, FOOTPRINT*>& aFootprintMap )
848{
849 // Verify that board contains all pads in netlist: if it doesn't then footprints are
850 // wrong or missing.
851
852 wxString msg;
853 wxString padNumber;
854
855 for( int i = 0; i < (int) aNetlist.GetCount(); i++ )
856 {
857 COMPONENT* component = aNetlist.GetComponent( i );
858 FOOTPRINT* footprint = aFootprintMap[component];
859
860 if( !footprint ) // It can be missing in partial designs
861 continue;
862
863 // Explore all pins/pads in component
864 for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
865 {
866 padNumber = component->GetNet( jj ).GetPinName();
867
868 if( padNumber.IsEmpty() )
869 {
870 // bad symbol, report error
871 msg.Printf( _( "Symbol %s has pins with no number. These pins can not be matched "
872 "to pads in %s." ),
873 component->GetReference(),
874 footprint->GetFPID().Format().wx_str() );
876 ++m_errorCount;
877 }
878 else if( !footprint->FindPadByNumber( padNumber ) )
879 {
880 // not found: bad footprint, report error
881 msg.Printf( _( "%s pad %s not found in %s." ),
882 component->GetReference(),
883 padNumber,
884 footprint->GetFPID().Format().wx_str() );
886 ++m_errorCount;
887 }
888 }
889 }
890
891 return true;
892}
893
894
896{
897 FOOTPRINT* lastPreexistingFootprint = nullptr;
898 COMPONENT* component = nullptr;
899 wxString msg;
900
901 m_errorCount = 0;
902 m_warningCount = 0;
904
905 std::map<COMPONENT*, FOOTPRINT*> footprintMap;
906
907 if( !m_board->Footprints().empty() )
908 lastPreexistingFootprint = m_board->Footprints().back();
909
911
912 // First mark all nets (except <no net>) as stale; we'll update those which are current
913 // in the following two loops.
914 //
915 if( !m_isDryRun )
916 {
917 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
918 net->SetIsCurrent( net->GetNetCode() == 0 );
919 }
920
921 // Next go through the netlist updating all board footprints which have matching component
922 // entries and adding new footprints for those that don't.
923 //
924 for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
925 {
926 component = aNetlist.GetComponent( i );
927
928 if( component->GetProperties().count( wxT( "exclude_from_board" ) ) )
929 continue;
930
931 msg.Printf( _( "Processing symbol '%s:%s'." ),
932 component->GetReference(),
933 component->GetFPID().Format().wx_str() );
935
936 int matchCount = 0;
937
938 for( FOOTPRINT* footprint : m_board->Footprints() )
939 {
940 bool match = false;
941
943 {
944 for( const KIID& uuid : component->GetKIIDs() )
945 {
946 KIID_PATH base = component->GetPath();
947 base.push_back( uuid );
948
949 if( footprint->GetPath() == base )
950 {
951 match = true;
952 break;
953 }
954 }
955 }
956 else
957 {
958 match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
959 }
960
961 if( match )
962 {
963 FOOTPRINT* tmp = footprint;
964
965 if( m_replaceFootprints && component->GetFPID() != footprint->GetFPID() )
966 tmp = replaceFootprint( aNetlist, footprint, component );
967
968 if( tmp )
969 {
970 footprintMap[ component ] = tmp;
971
972 updateFootprintParameters( tmp, component );
973 updateComponentPadConnections( tmp, component );
974 }
975
976 matchCount++;
977 }
978
979 if( footprint == lastPreexistingFootprint )
980 {
981 // No sense going through the newly-created footprints: end of loop
982 break;
983 }
984 }
985
986 if( matchCount == 0 )
987 {
988 FOOTPRINT* footprint = addNewFootprint( component );
989
990 if( footprint )
991 {
992 footprintMap[ component ] = footprint;
993
994 updateFootprintParameters( footprint, component );
995 updateComponentPadConnections( footprint, component );
996 }
997 }
998 else if( matchCount > 1 )
999 {
1000 msg.Printf( _( "Multiple footprints found for '%s'." ), component->GetReference() );
1002 m_errorCount++;
1003 }
1004 }
1005
1006 updateCopperZoneNets( aNetlist );
1007
1008 // Finally go through the board footprints and update all those that *don't* have matching
1009 // component entries.
1010 //
1011 for( FOOTPRINT* footprint : m_board->Footprints() )
1012 {
1013 bool matched = false;
1014 bool doDelete = m_deleteUnusedFootprints;
1015
1016 if( ( footprint->GetAttributes() & FP_BOARD_ONLY ) > 0 )
1017 doDelete = false;
1018
1020 component = aNetlist.GetComponentByPath( footprint->GetPath() );
1021 else
1022 component = aNetlist.GetComponentByReference( footprint->GetReference() );
1023
1024 if( component && component->GetProperties().count( wxT( "exclude_from_board" ) ) == 0 )
1025 matched = true;
1026
1027 if( doDelete && !matched && footprint->IsLocked() )
1028 {
1029 if( m_isDryRun )
1030 {
1031 msg.Printf( _( "Cannot remove unused footprint %s (locked)." ),
1032 footprint->GetReference() );
1033 }
1034 else
1035 {
1036 msg.Printf( _( "Could not remove unused footprint %s (locked)." ),
1037 footprint->GetReference() );
1038 }
1039
1041 m_errorCount++;
1042 doDelete = false;
1043 }
1044
1045 if( doDelete && !matched )
1046 {
1047 if( m_isDryRun )
1048 {
1049 msg.Printf( _( "Remove unused footprint %s." ), footprint->GetReference() );
1050 }
1051 else
1052 {
1053 m_commit.Remove( footprint );
1054 msg.Printf( _( "Removed unused footprint %s." ), footprint->GetReference() );
1055 }
1056
1058 }
1059 else if( !m_isDryRun )
1060 {
1061 if( !matched )
1062 footprint->SetPath( KIID_PATH() );
1063
1064 for( PAD* pad : footprint->Pads() )
1065 {
1066 if( pad->GetNet() )
1067 pad->GetNet()->SetIsCurrent( true );
1068 }
1069 }
1070 }
1071
1072 if( !m_isDryRun )
1073 {
1075 testConnectivity( aNetlist, footprintMap );
1076
1077 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
1078 {
1079 if( !net->IsCurrent() )
1080 {
1081 msg.Printf( _( "Removed unused net %s." ), net->GetNetname() );
1083 m_commit.Removed( net );
1084 }
1085 }
1086
1088
1089 // When new footprints are added, the automatic zone refill is disabled because:
1090 // * it creates crashes when calculating dynamic ratsnests if auto refill is enabled.
1091 // (the auto refills rebuild the connectivity with incomplete data)
1092 // * it is useless because zones will be refilled after placing new footprints
1093 m_commit.Push( _( "Update netlist" ), m_newFootprintsCount ? ZONE_FILL_OP : 0 );
1094
1096
1097 // Although m_commit will probably also set this, it's not guaranteed, and we need to make
1098 // sure any modification to netclasses gets persisted to project settings through a save.
1099 m_frame->OnModify();
1100 }
1101
1102 if( m_isDryRun )
1103 {
1104 for( const std::pair<const wxString, NETINFO_ITEM*>& addedNet : m_addedNets )
1105 delete addedNet.second;
1106
1107 m_addedNets.clear();
1108 }
1109
1110 // Update the ratsnest
1113
1114 msg.Printf( _( "Total warnings: %d, errors: %d." ), m_warningCount, m_errorCount );
1116
1117 return true;
1118}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
#define ZONE_FILL_OP
Definition: board_commit.h:43
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
std::map< PAD *, wxString > m_padNets
void cacheNetname(PAD *aPad, const wxString &aNetname)
bool UpdateNetlist(NETLIST &aNetlist)
Update the board's components according to the new netlist.
wxString getNetname(PAD *aPad)
wxString getPinFunction(PAD *aPad)
std::vector< FOOTPRINT * > m_addedFootprints
std::map< wxString, wxString > m_oldToNewNets
bool updateFootprintParameters(FOOTPRINT *aPcbFootprint, COMPONENT *aNetlistComponent)
std::map< wxString, NETINFO_ITEM * > m_addedNets
bool testConnectivity(NETLIST &aNetlist, std::map< COMPONENT *, FOOTPRINT * > &aFootprintMap)
bool updateCopperZoneNets(NETLIST &aNetlist)
void cachePinFunction(PAD *aPad, const wxString &aPinFunction)
BOARD_NETLIST_UPDATER(PCB_EDIT_FRAME *aFrame, BOARD *aBoard)
bool updateComponentPadConnections(FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
std::map< PAD *, wxString > m_padPinFunctions
std::map< ZONE *, std::vector< PAD * > > m_zoneConnectionsCache
FOOTPRINT * replaceFootprint(NETLIST &aNetlist, FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
FOOTPRINT * addNewFootprint(COMPONENT *aComponent)
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:270
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:800
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:858
const PAGE_INFO & GetPageSettings() const
Definition: board.h:638
ZONES & Zones()
Definition: board.h:318
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:1533
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:168
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:1601
FOOTPRINTS & Footprints()
Definition: board.h:312
TRACKS & Tracks()
Definition: board.h:309
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:498
bool IsEmpty() const
Definition: board.h:363
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:432
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetWidth() const
Definition: box2.h:187
Vec Centre() const
Definition: box2.h:70
coord_type GetBottom() const
Definition: box2.h:190
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
Definition: commit.h:91
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:104
COMMIT & Modified(EDA_ITEM *aItem, EDA_ITEM *aCopy, BASE_SCREEN *aScreen=nullptr)
Definition: commit.h:111
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:79
COMMIT & Removed(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:97
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Definition: commit.cpp:124
Used to store the component pin name to net name (and pin function) associations stored in a netlist.
Definition: pcb_netlist.h:44
const wxString & GetNetName() const
Definition: pcb_netlist.h:58
bool IsValid() const
Definition: pcb_netlist.h:62
const wxString & GetPinFunction() const
Definition: pcb_netlist.h:59
const wxString & GetPinName() const
Definition: pcb_netlist.h:57
const wxString & GetPinType() const
Definition: pcb_netlist.h:60
Store all of the related footprint information found in a netlist.
Definition: pcb_netlist.h:85
const COMPONENT_NET & GetNet(unsigned aIndex) const
Definition: pcb_netlist.h:111
const KIID_PATH & GetPath() const
Definition: pcb_netlist.h:143
const wxString & GetReference() const
Definition: pcb_netlist.h:126
const wxString & GetValue() const
Definition: pcb_netlist.h:129
const std::map< wxString, wxString > & GetProperties() const
Definition: pcb_netlist.h:135
const std::vector< KIID > & GetKIIDs() const
Definition: pcb_netlist.h:145
const LIB_ID & GetFPID() const
Definition: pcb_netlist.h:138
unsigned GetNetCount() const
Definition: pcb_netlist.h:109
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:100
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:1652
void SetPath(const KIID_PATH &aPath)
Definition: footprint.h:227
void SetAttributes(int aAttributes)
Definition: footprint.h:253
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: footprint.cpp:1386
int GetAttributes() const
Definition: footprint.h:252
PADS & Pads()
Definition: footprint.h:172
const LIB_ID & GetFPID() const
Definition: footprint.h:214
void SetReference(const wxString &aReference)
Definition: footprint.h:530
void SetValue(const wxString &aValue)
Definition: footprint.h:557
const wxString & GetValue() const
Definition: footprint.h:549
const wxString & GetReference() const
Definition: footprint.h:521
void SetProperties(const std::map< wxString, wxString > &aProps)
Definition: footprint.h:577
const KIID_PATH & GetPath() const
Definition: footprint.h:226
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
Definition: footprint.cpp:1187
const std::map< wxString, wxString > & GetProperties() const
Definition: footprint.h:576
wxString AsString() const
Definition: kiid.cpp:359
Definition: kiid.h:48
bool empty() const
Definition: lib_id.h:193
UTF8 Format() const
Definition: lib_id.cpp:117
Handle the data for a net.
Definition: netinfo.h:67
void SetIsCurrent(bool isCurrent)
Definition: netinfo.h:163
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:387
void RemoveUnusedNets()
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
Definition: pcb_netlist.h:213
unsigned GetCount() const
Definition: pcb_netlist.h:234
COMPONENT * GetComponentByPath(const KIID_PATH &aPath)
Return a COMPONENT by aPath.
COMPONENT * GetComponentByReference(const wxString &aReference)
Return a COMPONENT by aReference.
COMPONENT * GetComponent(unsigned aIndex)
Return the COMPONENT at aIndex.
Definition: pcb_netlist.h:242
static REPORTER & GetInstance()
Definition: reporter.cpp:117
Definition: pad.h:59
const wxString & GetPinFunction() const
Definition: pad.h:146
const VECTOR2I GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition: page_info.h:162
DISPLAY_OPTIONS m_Display
PCBNEW_SETTINGS * GetPcbNewSettings() const
FOOTPRINT * LoadFootprint(const LIB_ID &aFootprintId)
Attempt to load aFootprintId from the footprint library table.
The main frame for Pcbnew.
void OnModify() override
Must be called after a board change to set the modified flag.
void ExchangeFootprint(FOOTPRINT *aExisting, FOOTPRINT *aNew, BOARD_COMMIT &aCommit, bool deleteExtraTexts=true, bool resetTextLayers=true, bool resetTextEffects=true, bool resetFabricationAttrs=true, bool reset3DModels=true, bool *aUpdated=nullptr)
Replace aExisting footprint by aNew footprint using the Existing footprint settings (position,...
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
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
wxString wx_str() const
Definition: utf8.cpp:46
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
The common library.
#define _(s)
@ FP_DNP
Definition: footprint.h:77
@ FP_BOARD_ONLY
Definition: footprint.h:73
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:72
@ FP_JUST_ADDED
Definition: footprint.h:74
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
wxString UnescapeString(const wxString &aSource)
const double IU_PER_MILS
Definition: base_units.h:78
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:93