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 The 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>
36#include <netinfo.h>
37#include <footprint.h>
38#include <pad.h>
39#include <pcb_group.h>
40#include <pcb_track.h>
41#include <zone.h>
42#include <string_utils.h>
43#include <pcbnew_settings.h>
44#include <pcb_edit_frame.h>
47#include <reporter.h>
48
50
51
53 m_frame( aFrame ),
54 m_commit( aFrame ),
55 m_board( aBoard )
56{
58
60 m_isDryRun = false;
62 m_lookupByTimestamp = false;
63 m_transferGroups = false;
64 m_overrideLocks = false;
65
67 m_errorCount = 0;
69}
70
71
73{
74}
75
76
77// These functions allow inspection of pad nets during dry runs by keeping a cache of
78// current pad netnames indexed by pad.
79
80void BOARD_NETLIST_UPDATER::cacheNetname( PAD* aPad, const wxString& aNetname )
81{
82 m_padNets[ aPad ] = aNetname;
83}
84
85
87{
88 if( m_isDryRun && m_padNets.count( aPad ) )
89 return m_padNets[ aPad ];
90 else
91 return aPad->GetNetname();
92}
93
94
95void BOARD_NETLIST_UPDATER::cachePinFunction( PAD* aPad, const wxString& aPinFunction )
96{
97 m_padPinFunctions[ aPad ] = aPinFunction;
98}
99
100
102{
103 if( m_isDryRun && m_padPinFunctions.count( aPad ) )
104 return m_padPinFunctions[ aPad ];
105 else
106 return aPad->GetPinFunction();
107}
108
109
111{
112 VECTOR2I bestPosition;
113
114 if( !m_board->IsEmpty() )
115 {
116 // Position new components below any existing board features.
118
119 if( bbox.GetWidth() || bbox.GetHeight() )
120 {
121 bestPosition.x = bbox.Centre().x;
122 bestPosition.y = bbox.GetBottom() + pcbIUScale.mmToIU( 10 );
123 }
124 }
125 else
126 {
127 // Position new components in the center of the page when the board is empty.
129
130 bestPosition.x = pageSize.x / 2;
131 bestPosition.y = pageSize.y / 2;
132 }
133
134 return bestPosition;
135}
136
137
139{
140 wxString msg;
141
142 if( aComponent->GetFPID().empty() )
143 {
144 msg.Printf( _( "Cannot add %s (no footprint assigned)." ),
145 aComponent->GetReference() );
147 ++m_errorCount;
148 return nullptr;
149 }
150
151 FOOTPRINT* footprint = m_frame->LoadFootprint( aComponent->GetFPID() );
152
153 if( footprint == nullptr )
154 {
155 msg.Printf( _( "Cannot add %s (footprint '%s' not found)." ),
156 aComponent->GetReference(),
157 EscapeHTML( aComponent->GetFPID().Format().wx_str() ) );
159 ++m_errorCount;
160 return nullptr;
161 }
162
163 footprint->SetStaticComponentClass(
165
166 if( m_isDryRun )
167 {
168 msg.Printf( _( "Add %s (footprint '%s')." ),
169 aComponent->GetReference(),
170 EscapeHTML( aComponent->GetFPID().Format().wx_str() ) );
171
172 delete footprint;
173 footprint = nullptr;
174 }
175 else
176 {
177 for( PAD* pad : footprint->Pads() )
178 {
179 // Set the pads ratsnest settings to the global settings
180 pad->SetLocalRatsnestVisible( m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest );
181
182 // Pads in the library all have orphaned nets. Replace with Default.
183 pad->SetNetCode( 0 );
184 }
185
186 footprint->SetParent( m_board );
188
189 // This flag is used to prevent connectivity from considering the footprint during its
190 // initial build after the footprint is committed, because we're going to immediately start
191 // a move operation on the footprint and don't want its pads to drive nets onto vias/tracks
192 // it happens to land on at the initial position.
193 footprint->SetAttributes( footprint->GetAttributes() | FP_JUST_ADDED );
194
195 m_addedFootprints.push_back( footprint );
196 m_commit.Add( footprint );
197
198 msg.Printf( _( "Added %s (footprint '%s')." ),
199 aComponent->GetReference(),
200 EscapeHTML( aComponent->GetFPID().Format().wx_str() ) );
201 }
202
205 return footprint;
206}
207
208
210{
211 wxString curClassName, newClassName;
212 COMPONENT_CLASS* newClass = nullptr;
213
214 if( const COMPONENT_CLASS* curClass = aFootprint->GetStaticComponentClass() )
215 curClassName = curClass->GetName();
216
217 // Calculate the new component class
218 if( m_isDryRun )
219 {
221 aNewComponent->GetComponentClassNames() );
222 }
223 else
224 {
226 aNewComponent->GetComponentClassNames() );
227 newClassName = newClass->GetName();
228 }
229
230 if( curClassName == newClassName )
231 return;
232
233 wxString msg;
234
235 if( m_isDryRun )
236 {
237 if( curClassName == wxEmptyString && newClassName != wxEmptyString )
238 {
239 msg.Printf( _( "Change %s component class to '%s'." ),
240 aFootprint->GetReference(),
241 EscapeHTML( newClassName ) );
242 }
243 else if( curClassName != wxEmptyString && newClassName == wxEmptyString )
244 {
245 msg.Printf( _( "Remove %s component class (currently '%s')." ),
246 aFootprint->GetReference(),
247 EscapeHTML( curClassName ) );
248 }
249 else
250 {
251 msg.Printf( _( "Change %s component class from '%s' to '%s'." ),
252 aFootprint->GetReference(),
253 EscapeHTML( curClassName ),
254 EscapeHTML( newClassName ) );
255 }
256 }
257 else
258 {
259 wxASSERT_MSG( newClass != nullptr, "Component class should not be nullptr" );
260
261 aFootprint->SetStaticComponentClass( newClass );
262
263 if( curClassName == wxEmptyString && newClassName != wxEmptyString )
264 {
265 msg.Printf( _( "Changed %s component class to '%s'." ),
266 aFootprint->GetReference(),
267 EscapeHTML( newClassName ) );
268 }
269 else if( curClassName != wxEmptyString && newClassName == wxEmptyString )
270 {
271 msg.Printf( _( "Removed %s component class (was '%s')." ),
272 aFootprint->GetReference(),
273 EscapeHTML( curClassName ) );
274 }
275 else
276 {
277 msg.Printf( _( "Changed %s component class from '%s' to '%s'." ),
278 aFootprint->GetReference(),
279 EscapeHTML( curClassName ),
280 EscapeHTML( newClassName ) );
281 }
282 }
283
285}
286
287
289 COMPONENT* aNewComponent )
290{
291 wxString msg;
292
293 if( aNewComponent->GetFPID().empty() )
294 {
295 msg.Printf( _( "Cannot update %s (no footprint assigned)." ),
296 aNewComponent->GetReference() );
298 ++m_errorCount;
299 return nullptr;
300 }
301
302 FOOTPRINT* newFootprint = m_frame->LoadFootprint( aNewComponent->GetFPID() );
303
304 if( newFootprint == nullptr )
305 {
306 msg.Printf( _( "Cannot update %s (footprint '%s' not found)." ),
307 aNewComponent->GetReference(),
308 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
310 ++m_errorCount;
311 return nullptr;
312 }
313
314 if( m_isDryRun )
315 {
316 if( aFootprint->IsLocked() && !m_overrideLocks )
317 {
318 msg.Printf( _( "Cannot change %s footprint from '%s' to '%s' (footprint is locked)."),
319 aFootprint->GetReference(),
320 EscapeHTML( aFootprint->GetFPID().Format().wx_str() ),
321 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
324 delete newFootprint;
325 return nullptr;
326 }
327 else
328 {
329 msg.Printf( _( "Change %s footprint from '%s' to '%s'."),
330 aFootprint->GetReference(),
331 EscapeHTML( aFootprint->GetFPID().Format().wx_str() ),
332 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
335 delete newFootprint;
336 return nullptr;
337 }
338 }
339 else
340 {
341 if( aFootprint->IsLocked() && !m_overrideLocks )
342 {
343 msg.Printf( _( "Could not change %s footprint from '%s' to '%s' (footprint is locked)."),
344 aFootprint->GetReference(),
345 EscapeHTML( aFootprint->GetFPID().Format().wx_str() ),
346 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
349 delete newFootprint;
350 return nullptr;
351 }
352 else
353 {
354 m_frame->ExchangeFootprint( aFootprint, newFootprint, m_commit );
355
356 msg.Printf( _( "Changed %s footprint from '%s' to '%s'."),
357 aFootprint->GetReference(),
358 EscapeHTML( aFootprint->GetFPID().Format().wx_str() ),
359 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
362 return newFootprint;
363 }
364 }
365}
366
367
369 COMPONENT* aNetlistComponent )
370{
371 wxString msg;
372
373 // Create a copy only if the footprint has not been added during this update
374 FOOTPRINT* copy = nullptr;
375
376 if( !m_commit.GetStatus( aPcbFootprint ) )
377 {
378 copy = static_cast<FOOTPRINT*>( aPcbFootprint->Clone() );
379 copy->SetParentGroup( nullptr );
380 }
381
382 bool changed = false;
383
384 // Test for reference designator field change.
385 if( aPcbFootprint->GetReference() != aNetlistComponent->GetReference() )
386 {
387 if( m_isDryRun )
388 {
389 msg.Printf( _( "Change %s reference designator to %s." ),
390 aPcbFootprint->GetReference(),
391 aNetlistComponent->GetReference() );
392 }
393 else
394 {
395 msg.Printf( _( "Changed %s reference designator to %s." ),
396 aPcbFootprint->GetReference(),
397 aNetlistComponent->GetReference() );
398
399 changed = true;
400 aPcbFootprint->SetReference( aNetlistComponent->GetReference() );
401 }
402
404 }
405
406 // Test for value field change.
407 if( aPcbFootprint->GetValue() != aNetlistComponent->GetValue() )
408 {
409 if( m_isDryRun )
410 {
411 msg.Printf( _( "Change %s value from %s to %s." ),
412 aPcbFootprint->GetReference(),
413 EscapeHTML( aPcbFootprint->GetValue() ),
414 EscapeHTML( aNetlistComponent->GetValue() ) );
415 }
416 else
417 {
418 msg.Printf( _( "Changed %s value from %s to %s." ),
419 aPcbFootprint->GetReference(),
420 EscapeHTML( aPcbFootprint->GetValue() ),
421 EscapeHTML( aNetlistComponent->GetValue() ) );
422
423 changed = true;
424 aPcbFootprint->SetValue( aNetlistComponent->GetValue() );
425 }
426
428 }
429
430 // Test for time stamp change.
431 KIID_PATH new_path = aNetlistComponent->GetPath();
432
433 if( !aNetlistComponent->GetKIIDs().empty() )
434 new_path.push_back( aNetlistComponent->GetKIIDs().front() );
435
436 if( aPcbFootprint->GetPath() != new_path )
437 {
438 if( m_isDryRun )
439 {
440 msg.Printf( _( "Update %s symbol association from %s to %s." ),
441 aPcbFootprint->GetReference(),
442 EscapeHTML( aPcbFootprint->GetPath().AsString() ),
443 EscapeHTML( new_path.AsString() ) );
444 }
445 else
446 {
447 msg.Printf( _( "Updated %s symbol association from %s to %s." ),
448 aPcbFootprint->GetReference(),
449 EscapeHTML( aPcbFootprint->GetPath().AsString() ),
450 EscapeHTML( new_path.AsString() ) );
451
452 changed = true;
453 aPcbFootprint->SetPath( new_path );
454 }
455
457 }
458
459 nlohmann::ordered_map<wxString, wxString> fpFieldsAsMap;
460
461 for( PCB_FIELD* field : aPcbFootprint->GetFields() )
462 {
463 // These fields are individually checked above
464 if( field->IsReference() || field->IsValue() || field->IsComponentClass() )
465 {
466 continue;
467 }
468
469 fpFieldsAsMap[field->GetName()] = field->GetText();
470 }
471
472 // Remove the ref/value/footprint fields that are individually handled
473 nlohmann::ordered_map<wxString, wxString> compFields = aNetlistComponent->GetFields();
474 compFields.erase( GetCanonicalFieldName( FIELD_T::REFERENCE ) );
475 compFields.erase( GetCanonicalFieldName( FIELD_T::VALUE ) );
476 compFields.erase( GetCanonicalFieldName( FIELD_T::FOOTPRINT ) );
477
478 // Remove any component class fields - these are not editable in the pcb editor
479 compFields.erase( wxT( "Component Class" ) );
480
481 // Fields are stored as an ordered map, but we don't (yet) support reordering
482 // the footprint fields to match the symbol, so we manually check the fields
483 // in the order they are stored in the symbol.
484 bool same = true;
485
486 for( const auto& [name, value] : compFields )
487 {
488 if( fpFieldsAsMap.count( name ) == 0 || fpFieldsAsMap[name] != value )
489 {
490 same = false;
491 break;
492 }
493 }
494
495 for( const auto& [name, value] : fpFieldsAsMap )
496 {
497 if( compFields.count( name ) == 0 )
498 {
499 same = false;
500 break;
501 }
502 }
503
504 if( !same )
505 {
506 if( m_isDryRun )
507 {
508 msg.Printf( _( "Update %s fields." ), aPcbFootprint->GetReference() );
510
511 // Remove fields that aren't present in the symbol
512 for( PCB_FIELD* field : aPcbFootprint->GetFields() )
513 {
514 if( field->IsMandatory() )
515 continue;
516
517 if( compFields.count( field->GetName() ) == 0 )
518 {
519 msg.Printf( _( "Remove %s footprint fields not in symbol." ),
520 aPcbFootprint->GetReference() );
522 break;
523 }
524 }
525 }
526 else
527 {
528 msg.Printf( _( "Updated %s fields." ), aPcbFootprint->GetReference() );
530
531 changed = true;
532
533 // Add or change field value
534 for( auto& [name, value] : compFields )
535 {
536 if( aPcbFootprint->HasField( name ) )
537 {
538 aPcbFootprint->GetField( name )->SetText( value );
539 }
540 else
541 {
542 PCB_FIELD* newField = new PCB_FIELD( aPcbFootprint, FIELD_T::USER );
543 aPcbFootprint->Add( newField );
544
545 newField->SetName( name );
546 newField->SetText( value );
547 newField->SetVisible( false );
548 newField->SetLayer( aPcbFootprint->GetLayer() == F_Cu ? F_Fab : B_Fab );
549
550 // Give the relative position (0,0) in footprint
551 newField->SetPosition( aPcbFootprint->GetPosition() );
552 // Give the footprint orientation
553 newField->Rotate( aPcbFootprint->GetPosition(), aPcbFootprint->GetOrientation() );
554
555 if( m_frame )
557 }
558 }
559
560 // Remove and delete fields that aren't present in the symbol
561 bool warned = false;
562
563 std::vector<PCB_FIELD*> fieldList;
564 aPcbFootprint->GetFields( fieldList, false );
565
566 for( PCB_FIELD* field : fieldList )
567 {
568 if( field->IsMandatory() )
569 continue;
570
571 if( compFields.count( field->GetName() ) == 0 )
572 {
573 if( !warned )
574 {
575 warned = true;
576 msg.Printf( _( "Removed %s footprint fields not in symbol." ),
577 aPcbFootprint->GetReference() );
579 }
580
581 aPcbFootprint->Remove( field );
582
583 if( m_frame )
584 m_frame->GetCanvas()->GetView()->Remove( field );
585
586 delete field;
587 }
588 }
589 }
590 }
591
592 wxString sheetname;
593 wxString sheetfile;
594 wxString fpFilters;
595
596 wxString humanSheetPath = aNetlistComponent->GetHumanReadablePath();
597
598 if( !humanSheetPath.empty() )
599 sheetname = humanSheetPath;
600 else if( aNetlistComponent->GetProperties().count( wxT( "Sheetname" ) ) > 0 )
601 sheetname = aNetlistComponent->GetProperties().at( wxT( "Sheetname" ) );
602
603 if( aNetlistComponent->GetProperties().count( wxT( "Sheetfile" ) ) > 0 )
604 sheetfile = aNetlistComponent->GetProperties().at( wxT( "Sheetfile" ) );
605
606 if( aNetlistComponent->GetProperties().count( wxT( "ki_fp_filters" ) ) > 0 )
607 fpFilters = aNetlistComponent->GetProperties().at( wxT( "ki_fp_filters" ) );
608
609 if( sheetname != aPcbFootprint->GetSheetname() )
610 {
611 if( m_isDryRun )
612 {
613 msg.Printf( _( "Update %s sheetname to '%s'." ),
614 aPcbFootprint->GetReference(),
615 EscapeHTML( sheetname ) );
616 }
617 else
618 {
619 aPcbFootprint->SetSheetname( sheetname );
620 msg.Printf( _( "Updated %s sheetname to '%s'." ),
621 aPcbFootprint->GetReference(),
622 EscapeHTML( sheetname ) );
623 }
624
626 }
627
628 if( sheetfile != aPcbFootprint->GetSheetfile() )
629 {
630 if( m_isDryRun )
631 {
632 msg.Printf( _( "Update %s sheetfile to '%s'." ),
633 aPcbFootprint->GetReference(),
634 EscapeHTML( sheetfile ) );
635 }
636 else
637 {
638 aPcbFootprint->SetSheetfile( sheetfile );
639 msg.Printf( _( "Updated %s sheetfile to '%s'." ),
640 aPcbFootprint->GetReference(),
641 EscapeHTML( sheetfile ) );
642 }
643
645 }
646
647 if( fpFilters != aPcbFootprint->GetFilters() )
648 {
649 if( m_isDryRun )
650 {
651 msg.Printf( _( "Update %s footprint filters to '%s'." ),
652 aPcbFootprint->GetReference(),
653 EscapeHTML( fpFilters ) );
654 }
655 else
656 {
657 aPcbFootprint->SetFilters( fpFilters );
658 msg.Printf( _( "Updated %s footprint filters to '%s'." ),
659 aPcbFootprint->GetReference(),
660 EscapeHTML( fpFilters ) );
661 }
662
664 }
665
666 if( ( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) > 0 )
667 != ( ( aPcbFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 ) )
668 {
669 if( m_isDryRun )
670 {
671 if( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) )
672 {
673 msg.Printf( _( "Add %s 'exclude from BOM' fabrication attribute." ),
674 aPcbFootprint->GetReference() );
675 }
676 else
677 {
678 msg.Printf( _( "Remove %s 'exclude from BOM' fabrication attribute." ),
679 aPcbFootprint->GetReference() );
680 }
681 }
682 else
683 {
684 int attributes = aPcbFootprint->GetAttributes();
685
686 if( aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) )
687 {
688 attributes |= FP_EXCLUDE_FROM_BOM;
689 msg.Printf( _( "Added %s 'exclude from BOM' fabrication attribute." ),
690 aPcbFootprint->GetReference() );
691 }
692 else
693 {
694 attributes &= ~FP_EXCLUDE_FROM_BOM;
695 msg.Printf( _( "Removed %s 'exclude from BOM' fabrication attribute." ),
696 aPcbFootprint->GetReference() );
697 }
698
699 changed = true;
700 aPcbFootprint->SetAttributes( attributes );
701 }
702
704 }
705
706 if( ( aNetlistComponent->GetProperties().count( wxT( "dnp" ) ) > 0 )
707 != ( ( aPcbFootprint->GetAttributes() & FP_DNP ) > 0 ) )
708 {
709 if( m_isDryRun )
710 {
711 if( aNetlistComponent->GetProperties().count( wxT( "dnp" ) ) )
712 {
713 msg.Printf( _( "Add %s 'Do not place' fabrication attribute." ),
714 aPcbFootprint->GetReference() );
715 }
716 else
717 {
718 msg.Printf( _( "Remove %s 'Do not place' fabrication attribute." ),
719 aPcbFootprint->GetReference() );
720 }
721 }
722 else
723 {
724 int attributes = aPcbFootprint->GetAttributes();
725
726 if( aNetlistComponent->GetProperties().count( wxT( "dnp" ) ) )
727 {
728 attributes |= FP_DNP;
729 msg.Printf( _( "Added %s 'Do not place' fabrication attribute." ),
730 aPcbFootprint->GetReference() );
731 }
732 else
733 {
734 attributes &= ~FP_DNP;
735 msg.Printf( _( "Removed %s 'Do not place' fabrication attribute." ),
736 aPcbFootprint->GetReference() );
737 }
738
739 changed = true;
740 aPcbFootprint->SetAttributes( attributes );
741 }
742
744 }
745
746 if( aNetlistComponent->GetDuplicatePadNumbersAreJumpers()
747 != aPcbFootprint->GetDuplicatePadNumbersAreJumpers() )
748 {
749 bool value = aNetlistComponent->GetDuplicatePadNumbersAreJumpers();
750
751 if( !m_isDryRun )
752 {
753 changed = true;
754 aPcbFootprint->SetDuplicatePadNumbersAreJumpers( value );
755
756 if( value )
757 {
758 msg.Printf( _( "Added %s 'duplicate pad numbers are jumpers' attribute." ),
759 aPcbFootprint->GetReference() );
760 }
761 else
762 {
763 msg.Printf( _( "Removed %s 'duplicate pad numbers are jumpers' attribute." ),
764 aPcbFootprint->GetReference() );
765 }
766 }
767 else
768 {
769 if( value )
770 {
771 msg.Printf( _( "Add %s 'duplicate pad numbers are jumpers' attribute." ),
772 aPcbFootprint->GetReference() );
773 }
774 else
775 {
776 msg.Printf( _( "Remove %s 'duplicate pad numbers are jumpers' attribute." ),
777 aPcbFootprint->GetReference() );
778 }
779 }
780
782 }
783
784 if( aNetlistComponent->JumperPadGroups() != aPcbFootprint->JumperPadGroups() )
785 {
786 if( !m_isDryRun )
787 {
788 changed = true;
789 aPcbFootprint->JumperPadGroups() = aNetlistComponent->JumperPadGroups();
790 msg.Printf( _( "Updated %s jumper pad groups" ), aPcbFootprint->GetReference() );
791 }
792 else
793 {
794 msg.Printf( _( "Update %s jumper pad groups" ), aPcbFootprint->GetReference() );
795 }
796
798 }
799
800 if( changed && copy )
801 m_commit.Modified( aPcbFootprint, copy );
802 else if( copy )
803 delete copy;
804
805 return true;
806}
807
808
810 COMPONENT* aNetlistComponent )
811{
812 if( !m_transferGroups )
813 return false;
814
815 wxString msg;
816
817 // Create a copy only if the footprint has not been added during this update
818 FOOTPRINT* copy = nullptr;
819
820 if( !m_commit.GetStatus( aPcbFootprint ) )
821 {
822 copy = static_cast<FOOTPRINT*>( aPcbFootprint->Clone() );
823 copy->SetParentGroup( nullptr );
824 }
825
826 bool changed = false;
827
828 // These hold the info for group and group KIID coming from the netlist
829 // newGroup may point to an existing group on the board if we find an
830 // incoming group UUID that matches an existing group
831 PCB_GROUP* newGroup = nullptr;
832 KIID newGroupKIID = aNetlistComponent->GetGroup() ? aNetlistComponent->GetGroup()->uuid : 0;
833
834 PCB_GROUP* existingGroup = static_cast<PCB_GROUP*>( aPcbFootprint->GetParentGroup() );
835 KIID existingGroupKIID = existingGroup ? existingGroup->m_Uuid : 0;
836
837 // Find existing group based on matching UUIDs
838 auto it = std::find_if( m_board->Groups().begin(), m_board->Groups().end(),
839 [&](PCB_GROUP* group) {
840 return group->m_Uuid == newGroupKIID;
841 });
842
843 // If we find a group with the same UUID, use it
844 if( it != m_board->Groups().end() )
845 newGroup = *it;
846
847 // No changes, nothing to do
848 if( newGroupKIID == existingGroupKIID )
849 return changed;
850
851 // Remove from existing group
852 if( existingGroupKIID != 0 )
853 {
854 if( m_isDryRun )
855 {
856 msg.Printf( _( "Remove %s from group \"%s\"." ),
857 aPcbFootprint->GetReference(),
858 EscapeHTML( existingGroup->GetName() ) );
859 }
860 else
861 {
862 msg.Printf( _( "Removed %s from group \"%s\"." ),
863 aPcbFootprint->GetReference(),
864 EscapeHTML( existingGroup->GetName() ) );
865
866 changed = true;
867 m_commit.Modify( aPcbFootprint->GetParentGroup()->AsEdaItem() );
868 aPcbFootprint->GetParentGroup()->RemoveItem( aPcbFootprint );
869 aPcbFootprint->SetParentGroup( nullptr );
870 }
871
873 }
874
875 // Add to new group
876 if( newGroupKIID != 0 )
877 {
878 if( m_isDryRun )
879 {
880 msg.Printf( _( "Add %s to group \"%s\"." ),
881 aPcbFootprint->GetReference(),
882 EscapeHTML( aNetlistComponent->GetGroup()->name ) );
883 }
884 else
885 {
886 msg.Printf( _( "Added %s group \"%s\"." ),
887 aPcbFootprint->GetReference(),
888 EscapeHTML( aNetlistComponent->GetGroup()->name ) );
889
890 changed = true;
891
892 if( newGroup == nullptr )
893 {
894 newGroup = new PCB_GROUP( m_board );
895 const_cast<KIID&>( newGroup->m_Uuid ) = newGroupKIID;
896 newGroup->SetName( aNetlistComponent->GetGroup()->name );
897
898 // Add the group to the board manually so we can find it by checking
899 // board groups for later footprints that are checking for existing groups
900 m_board->Add( newGroup );
901 m_commit.Added( newGroup );
902 }
903 else
904 {
905 m_commit.Modify( newGroup->AsEdaItem() );
906 }
907
908 newGroup->AddItem( aPcbFootprint );
909 aPcbFootprint->SetParentGroup( newGroup );
910 }
911
913 }
914
915 if( changed && copy )
916 m_commit.Modified( aPcbFootprint, copy );
917 else if( copy )
918 delete copy;
919
920 return changed;
921}
922
923
925 COMPONENT* aNewComponent )
926{
927 wxString msg;
928
929 // Create a copy only if the footprint has not been added during this update
930 FOOTPRINT* copy = nullptr;
931
932 if( !m_isDryRun && !m_commit.GetStatus( aFootprint ) )
933 {
934 copy = static_cast<FOOTPRINT*>( aFootprint->Clone() );
935 copy->SetParentGroup( nullptr );
936 }
937
938 bool changed = false;
939
940 // At this point, the component footprint is updated. Now update the nets.
941 std::deque<PAD*> pads = aFootprint->Pads();
942 std::set<wxString> padNetnames;
943
944 std::sort( pads.begin(), pads.end(),
945 []( PAD* a, PAD* b )
946 {
947 return a->m_Uuid < b->m_Uuid;
948 } );
949
950 for( PAD* pad : pads )
951 {
952 const COMPONENT_NET& net = aNewComponent->GetNet( pad->GetNumber() );
953
954 wxString pinFunction;
955 wxString pinType;
956
957 if( net.IsValid() ) // i.e. the pad has a name
958 {
959 pinFunction = net.GetPinFunction();
960 pinType = net.GetPinType();
961 }
962
963 if( !m_isDryRun )
964 {
965 if( pad->GetPinFunction() != pinFunction )
966 {
967 changed = true;
968 pad->SetPinFunction( pinFunction );
969 }
970
971 if( pad->GetPinType() != pinType )
972 {
973 changed = true;
974 pad->SetPinType( pinType );
975 }
976 }
977 else
978 {
979 cachePinFunction( pad, pinFunction );
980 }
981
982 // Test if new footprint pad has no net (pads not on copper layers have no net).
983 if( !net.IsValid() || !pad->IsOnCopperLayer() )
984 {
985 if( !pad->GetNetname().IsEmpty() )
986 {
987 if( m_isDryRun )
988 {
989 msg.Printf( _( "Disconnect %s pin %s." ),
990 aFootprint->GetReference(),
991 EscapeHTML( pad->GetNumber() ) );
992 }
993 else
994 {
995 msg.Printf( _( "Disconnected %s pin %s." ),
996 aFootprint->GetReference(),
997 EscapeHTML( pad->GetNumber() ) );
998 }
999
1001 }
1002 else if( pad->IsOnCopperLayer() && !pad->GetNumber().IsEmpty() )
1003 {
1004 // pad is connectable but has no net found in netlist
1005 msg.Printf( _( "No net found for component %s pad %s (no pin %s in symbol)." ),
1006 aFootprint->GetReference(),
1007 EscapeHTML( pad->GetNumber() ),
1008 EscapeHTML( pad->GetNumber() ) );
1011 }
1012
1013 if( !m_isDryRun )
1014 {
1015 changed = true;
1016 pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
1017
1018 // If the pad has no net from netlist (i.e. not in netlist
1019 // it cannot have a pin function
1020 if( pad->GetNetname().IsEmpty() )
1021 pad->SetPinFunction( wxEmptyString );
1022
1023 }
1024 else
1025 {
1026 cacheNetname( pad, wxEmptyString );
1027 }
1028 }
1029 else // New footprint pad has a net.
1030 {
1031 wxString netName = net.GetNetName();
1032
1033 if( pad->IsNoConnectPad() )
1034 {
1035 netName = wxString::Format( wxS( "%s" ),
1036 EscapeHTML( net.GetNetName() ) );
1037
1038 for( int jj = 1; !padNetnames.insert( netName ).second; jj++ )
1039 {
1040 netName = wxString::Format( wxS( "%s_%d" ),
1041 EscapeHTML( net.GetNetName() ), jj );
1042 }
1043 }
1044
1045 NETINFO_ITEM* netinfo = m_board->FindNet( netName );
1046
1047 if( netinfo && !m_isDryRun )
1048 netinfo->SetIsCurrent( true );
1049
1050 if( pad->GetNetname() != netName )
1051 {
1052
1053 if( netinfo == nullptr )
1054 {
1055 // It might be a new net that has not been added to the board yet
1056 if( m_addedNets.count( netName ) )
1057 netinfo = m_addedNets[ netName ];
1058 }
1059
1060 if( netinfo == nullptr )
1061 {
1062 netinfo = new NETINFO_ITEM( m_board, netName );
1063
1064 // It is a new net, we have to add it
1065 if( !m_isDryRun )
1066 {
1067 changed = true;
1068 m_commit.Add( netinfo );
1069 }
1070
1071 m_addedNets[netName] = netinfo;
1072 msg.Printf( _( "Add net %s." ),
1073 EscapeHTML( UnescapeString( netName ) ) );
1075 }
1076
1077 if( !pad->GetNetname().IsEmpty() )
1078 {
1079 m_oldToNewNets[ pad->GetNetname() ] = netName;
1080
1081 if( m_isDryRun )
1082 {
1083 msg.Printf( _( "Reconnect %s pin %s from %s to %s."),
1084 aFootprint->GetReference(),
1085 EscapeHTML( pad->GetNumber() ),
1086 EscapeHTML( UnescapeString( pad->GetNetname() ) ),
1087 EscapeHTML( UnescapeString( netName ) ) );
1088 }
1089 else
1090 {
1091 msg.Printf( _( "Reconnected %s pin %s from %s to %s."),
1092 aFootprint->GetReference(),
1093 EscapeHTML( pad->GetNumber() ),
1094 EscapeHTML( UnescapeString( pad->GetNetname() ) ),
1095 EscapeHTML( UnescapeString( netName ) ) );
1096 }
1097 }
1098 else
1099 {
1100 if( m_isDryRun )
1101 {
1102 msg.Printf( _( "Connect %s pin %s to %s."),
1103 aFootprint->GetReference(),
1104 EscapeHTML( pad->GetNumber() ),
1105 EscapeHTML( UnescapeString( netName ) ) );
1106 }
1107 else
1108 {
1109 msg.Printf( _( "Connected %s pin %s to %s."),
1110 aFootprint->GetReference(),
1111 EscapeHTML( pad->GetNumber() ),
1112 EscapeHTML( UnescapeString( netName ) ) );
1113 }
1114 }
1115
1117
1118 if( !m_isDryRun )
1119 {
1120 changed = true;
1121 pad->SetNet( netinfo );
1122 }
1123 else
1124 {
1125 cacheNetname( pad, netName );
1126 }
1127 }
1128 }
1129 }
1130
1131 if( changed && copy )
1132 m_commit.Modified( aFootprint, copy );
1133 else if( copy )
1134 delete copy;
1135
1136 return true;
1137}
1138
1139
1141{
1142 for( ZONE* zone : m_board->Zones() )
1143 {
1144 if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
1145 continue;
1146
1147 m_zoneConnectionsCache[ zone ] = m_board->GetConnectivity()->GetConnectedPads( zone );
1148 }
1149}
1150
1151
1153{
1154 wxString msg;
1155 std::set<wxString> netlistNetnames;
1156
1157 for( int ii = 0; ii < (int) aNetlist.GetCount(); ii++ )
1158 {
1159 const COMPONENT* component = aNetlist.GetComponent( ii );
1160
1161 for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
1162 {
1163 const COMPONENT_NET& net = component->GetNet( jj );
1164 netlistNetnames.insert( net.GetNetName() );
1165 }
1166 }
1167
1168 for( PCB_TRACK* via : m_board->Tracks() )
1169 {
1170 if( via->Type() != PCB_VIA_T )
1171 continue;
1172
1173 if( netlistNetnames.count( via->GetNetname() ) == 0 )
1174 {
1175 wxString updatedNetname = wxEmptyString;
1176
1177 // Take via name from name change map if it didn't match to a new pad
1178 // (this is useful for stitching vias that don't connect to tracks)
1179 if( m_oldToNewNets.count( via->GetNetname() ) )
1180 {
1181 updatedNetname = m_oldToNewNets[via->GetNetname()];
1182 }
1183
1184 if( !updatedNetname.IsEmpty() )
1185 {
1186 if( m_isDryRun )
1187 {
1188 wxString originalNetname = via->GetNetname();
1189
1190 msg.Printf( _( "Reconnect via from %s to %s." ),
1191 EscapeHTML( UnescapeString( originalNetname ) ),
1192 EscapeHTML( UnescapeString( updatedNetname ) ) );
1193
1195 }
1196 else
1197 {
1198 NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
1199
1200 if( !netinfo )
1201 netinfo = m_addedNets[updatedNetname];
1202
1203 if( netinfo )
1204 {
1205 wxString originalNetname = via->GetNetname();
1206
1207 m_commit.Modify( via );
1208 via->SetNet( netinfo );
1209
1210 msg.Printf( _( "Reconnected via from %s to %s." ),
1211 EscapeHTML( UnescapeString( originalNetname ) ),
1212 EscapeHTML( UnescapeString( updatedNetname ) ) );
1213
1215 }
1216 }
1217 }
1218 else
1219 {
1220 msg.Printf( _( "Via connected to unknown net (%s)." ),
1221 EscapeHTML( UnescapeString( via->GetNetname() ) ) );
1224 }
1225 }
1226 }
1227
1228 // Test copper zones to detect "dead" nets (nets without any pad):
1229 for( ZONE* zone : m_board->Zones() )
1230 {
1231 if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
1232 continue;
1233
1234 if( netlistNetnames.count( zone->GetNetname() ) == 0 )
1235 {
1236 // Look for a pad in the zone's connected-pad-cache which has been updated to
1237 // a new net and use that. While this won't always be the right net, the dead
1238 // net is guaranteed to be wrong.
1239 wxString updatedNetname = wxEmptyString;
1240
1241 for( PAD* pad : m_zoneConnectionsCache[ zone ] )
1242 {
1243 if( getNetname( pad ) != zone->GetNetname() )
1244 {
1245 updatedNetname = getNetname( pad );
1246 break;
1247 }
1248 }
1249
1250 // Take zone name from name change map if it didn't match to a new pad
1251 // (this is useful for zones on internal layers)
1252 if( updatedNetname.IsEmpty() && m_oldToNewNets.count( zone->GetNetname() ) )
1253 {
1254 updatedNetname = m_oldToNewNets[ zone->GetNetname() ];
1255 }
1256
1257 if( !updatedNetname.IsEmpty() )
1258 {
1259 if( m_isDryRun )
1260 {
1261 wxString originalNetname = zone->GetNetname();
1262
1263 if( !zone->GetZoneName().IsEmpty() )
1264 {
1265 msg.Printf( _( "Reconnect copper zone '%s' from %s to %s." ),
1266 zone->GetZoneName(),
1267 EscapeHTML( UnescapeString( originalNetname ) ),
1268 EscapeHTML( UnescapeString( updatedNetname ) ) );
1269 }
1270 else
1271 {
1272 msg.Printf( _( "Reconnect copper zone from %s to %s." ),
1273 EscapeHTML( UnescapeString( originalNetname ) ),
1274 EscapeHTML( UnescapeString( updatedNetname ) ) );
1275 }
1276
1278 }
1279 else
1280 {
1281 NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
1282
1283 if( !netinfo )
1284 netinfo = m_addedNets[ updatedNetname ];
1285
1286 if( netinfo )
1287 {
1288 wxString originalNetname = zone->GetNetname();
1289
1290 m_commit.Modify( zone );
1291 zone->SetNet( netinfo );
1292
1293 if( !zone->GetZoneName().IsEmpty() )
1294 {
1295 msg.Printf( _( "Reconnected copper zone '%s' from %s to %s." ),
1296 EscapeHTML( zone->GetZoneName() ),
1297 EscapeHTML( UnescapeString( originalNetname ) ),
1298 EscapeHTML( UnescapeString( updatedNetname ) ) );
1299 }
1300 else
1301 {
1302 msg.Printf( _( "Reconnected copper zone from %s to %s." ),
1303 EscapeHTML( UnescapeString( originalNetname ) ),
1304 EscapeHTML( UnescapeString( updatedNetname ) ) );
1305 }
1306
1308 }
1309 }
1310 }
1311 else
1312 {
1313 if( !zone->GetZoneName().IsEmpty() )
1314 {
1315 msg.Printf( _( "Copper zone '%s' has no pads connected." ),
1316 EscapeHTML( zone->GetZoneName() ) );
1317 }
1318 else
1319 {
1320 PCB_LAYER_ID layer = zone->GetLayer();
1321 VECTOR2I pos = zone->GetPosition();
1322
1324 {
1326 pos.x *= -1;
1327
1329 pos.y *= -1;
1330 }
1331
1332 msg.Printf( _( "Copper zone on layer %s at (%s, %s) has no pads connected." ),
1333 EscapeHTML( m_board->GetLayerName( layer ) ),
1335 m_frame->MessageTextFromValue( pos.y ) );
1336 }
1337
1340 }
1341 }
1342 }
1343
1344 return true;
1345}
1346
1347
1349{
1350 if( !m_transferGroups )
1351 return false;
1352
1353 for( PCB_GROUP* pcbGroup : m_board->Groups() )
1354 {
1355 NETLIST_GROUP* netlistGroup = aNetlist.GetGroupByUuid( pcbGroup->m_Uuid );
1356
1357 if( netlistGroup == nullptr )
1358 continue;
1359
1360 if( netlistGroup->name != pcbGroup->GetName() )
1361 {
1362 if( m_isDryRun )
1363 {
1364 wxString msg;
1365 msg.Printf( _( "Change group name to '%s' to '%s'." ), EscapeHTML( pcbGroup->GetName() ),
1366 EscapeHTML( netlistGroup->name ) );
1368 }
1369 else
1370 {
1371 wxString msg;
1372 msg.Printf( _( "Changed group name to '%s' to '%s'." ), EscapeHTML( pcbGroup->GetName() ),
1373 EscapeHTML( netlistGroup->name ) );
1374 m_commit.Modify( pcbGroup->AsEdaItem() );
1375 pcbGroup->SetName( netlistGroup->name );
1376 }
1377 }
1378
1379 if( netlistGroup->libId != pcbGroup->GetDesignBlockLibId() )
1380 {
1381 if( m_isDryRun )
1382 {
1383 wxString msg;
1384 msg.Printf( _( "Change group library link to '%s'." ),
1385 EscapeHTML( netlistGroup->libId.GetUniStringLibId() ) );
1387 }
1388 else
1389 {
1390 wxString msg;
1391 msg.Printf( _( "Changed group library link to '%s'." ),
1392 EscapeHTML( netlistGroup->libId.GetUniStringLibId() ) );
1393 m_commit.Modify( pcbGroup->AsEdaItem() );
1394 pcbGroup->SetDesignBlockLibId( netlistGroup->libId );
1395 }
1396 }
1397 }
1398
1399 return true;
1400}
1401
1402
1404 std::map<COMPONENT*, FOOTPRINT*>& aFootprintMap )
1405{
1406 // Verify that board contains all pads in netlist: if it doesn't then footprints are
1407 // wrong or missing.
1408
1409 wxString msg;
1410 wxString padNumber;
1411
1412 for( int i = 0; i < (int) aNetlist.GetCount(); i++ )
1413 {
1414 COMPONENT* component = aNetlist.GetComponent( i );
1415 FOOTPRINT* footprint = aFootprintMap[component];
1416
1417 if( !footprint ) // It can be missing in partial designs
1418 continue;
1419
1420 // Explore all pins/pads in component
1421 for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
1422 {
1423 padNumber = component->GetNet( jj ).GetPinName();
1424
1425 if( padNumber.IsEmpty() )
1426 {
1427 // bad symbol, report error
1428 msg.Printf( _( "Symbol %s has pins with no number. These pins can not be matched "
1429 "to pads in %s." ),
1430 component->GetReference(),
1431 EscapeHTML( footprint->GetFPID().Format().wx_str() ) );
1433 ++m_errorCount;
1434 }
1435 else if( !footprint->FindPadByNumber( padNumber ) )
1436 {
1437 // not found: bad footprint, report error
1438 msg.Printf( _( "%s pad %s not found in %s." ),
1439 component->GetReference(),
1440 EscapeHTML( padNumber ),
1441 EscapeHTML( footprint->GetFPID().Format().wx_str() ) );
1443 ++m_errorCount;
1444 }
1445 }
1446 }
1447
1448 return true;
1449}
1450
1451
1453{
1454 FOOTPRINT* lastPreexistingFootprint = nullptr;
1455 COMPONENT* component = nullptr;
1456 wxString msg;
1457 std::unordered_set<wxString> sheetPaths;
1458
1459 m_errorCount = 0;
1460 m_warningCount = 0;
1462
1463 std::map<COMPONENT*, FOOTPRINT*> footprintMap;
1464
1465 if( !m_board->Footprints().empty() )
1466 lastPreexistingFootprint = m_board->Footprints().back();
1467
1469
1470 // First mark all nets (except <no net>) as stale; we'll update those which are current
1471 // in the following two loops. Also prepare the component class manager for updates.
1472 //
1473 if( !m_isDryRun )
1474 {
1475 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
1476 net->SetIsCurrent( net->GetNetCode() == 0 );
1477
1479 }
1480
1481 // Next go through the netlist updating all board footprints which have matching component
1482 // entries and adding new footprints for those that don't.
1483 //
1484 for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
1485 {
1486 component = aNetlist.GetComponent( i );
1487
1488 if( component->GetProperties().count( wxT( "exclude_from_board" ) ) )
1489 continue;
1490
1491 msg.Printf( _( "Processing symbol '%s:%s'." ),
1492 component->GetReference(),
1493 EscapeHTML( component->GetFPID().Format().wx_str() ) );
1495
1496 int matchCount = 0;
1497
1498 for( FOOTPRINT* footprint : m_board->Footprints() )
1499 {
1500 bool match = false;
1501
1503 {
1504 for( const KIID& uuid : component->GetKIIDs() )
1505 {
1506 KIID_PATH base = component->GetPath();
1507 base.push_back( uuid );
1508
1509 if( footprint->GetPath() == base )
1510 {
1511 match = true;
1512 break;
1513 }
1514 }
1515 }
1516 else
1517 {
1518 match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
1519 }
1520
1521 if( match )
1522 {
1523 FOOTPRINT* tmp = footprint;
1524
1525 if( m_replaceFootprints && component->GetFPID() != footprint->GetFPID() )
1526 tmp = replaceFootprint( aNetlist, footprint, component );
1527
1528 if( !tmp )
1529 tmp = footprint;
1530
1531 if( tmp )
1532 {
1533 footprintMap[ component ] = tmp;
1534
1535 updateFootprintParameters( tmp, component );
1536 updateFootprintGroup( tmp, component );
1537 updateComponentPadConnections( tmp, component );
1538 updateComponentClass( tmp, component );
1539
1540 sheetPaths.insert( footprint->GetSheetname() );
1541 }
1542
1543 matchCount++;
1544 }
1545
1546 if( footprint == lastPreexistingFootprint )
1547 {
1548 // No sense going through the newly-created footprints: end of loop
1549 break;
1550 }
1551 }
1552
1553 if( matchCount == 0 )
1554 {
1555 FOOTPRINT* footprint = addNewFootprint( component );
1556
1557 if( footprint )
1558 {
1559 footprintMap[ component ] = footprint;
1560
1561 updateFootprintParameters( footprint, component );
1562 updateFootprintGroup( footprint, component );
1563 updateComponentPadConnections( footprint, component );
1564 updateComponentClass( footprint, component );
1565
1566 sheetPaths.insert( footprint->GetSheetname() );
1567 }
1568 }
1569 else if( matchCount > 1 )
1570 {
1571 msg.Printf( _( "Multiple footprints found for %s." ),
1572 component->GetReference() );
1574 m_errorCount++;
1575 }
1576 }
1577
1578 updateCopperZoneNets( aNetlist );
1579 updateGroups( aNetlist );
1580
1581 // Finally go through the board footprints and update all those that *don't* have matching
1582 // component entries.
1583 //
1584 for( FOOTPRINT* footprint : m_board->Footprints() )
1585 {
1586 bool matched = false;
1587 bool doDelete = m_deleteUnusedFootprints;
1588
1589 if( ( footprint->GetAttributes() & FP_BOARD_ONLY ) > 0 )
1590 doDelete = false;
1591
1593 component = aNetlist.GetComponentByPath( footprint->GetPath() );
1594 else
1595 component = aNetlist.GetComponentByReference( footprint->GetReference() );
1596
1597 if( component && component->GetProperties().count( wxT( "exclude_from_board" ) ) == 0 )
1598 matched = true;
1599
1600 if( doDelete && !matched && footprint->IsLocked() && !m_overrideLocks )
1601 {
1602 if( m_isDryRun )
1603 {
1604 msg.Printf( _( "Cannot remove unused footprint %s (footprint is locked)." ),
1605 footprint->GetReference() );
1606 }
1607 else
1608 {
1609 msg.Printf( _( "Could not remove unused footprint %s (footprint is locked)." ),
1610 footprint->GetReference() );
1611 }
1612
1615 doDelete = false;
1616 }
1617
1618 if( doDelete && !matched )
1619 {
1620 if( m_isDryRun )
1621 {
1622 msg.Printf( _( "Remove unused footprint %s." ),
1623 footprint->GetReference() );
1624 }
1625 else
1626 {
1627 if( footprint->GetParentGroup() )
1628 m_commit.Stage( footprint, CHT_UNGROUP );
1629
1630 m_commit.Remove( footprint );
1631 msg.Printf( _( "Removed unused footprint %s." ),
1632 footprint->GetReference() );
1633 }
1634
1636 }
1637 else if( !m_isDryRun )
1638 {
1639 if( !matched )
1640 footprint->SetPath( KIID_PATH() );
1641
1642 for( PAD* pad : footprint->Pads() )
1643 {
1644 if( pad->GetNet() )
1645 pad->GetNet()->SetIsCurrent( true );
1646 }
1647 }
1648 }
1649
1650 if( !m_isDryRun )
1651 {
1652 // Finalise the component class manager
1654 m_board->SynchronizeComponentClasses( sheetPaths );
1655
1657 testConnectivity( aNetlist, footprintMap );
1658
1659 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
1660 {
1661 if( !net->IsCurrent() )
1662 {
1663 msg.Printf( _( "Removed unused net %s." ),
1664 EscapeHTML( net->GetNetname() ) );
1666 }
1667 }
1668
1670
1671 // When new footprints are added, the automatic zone refill is disabled because:
1672 // * it creates crashes when calculating dynamic ratsnests if auto refill is enabled.
1673 // (the auto refills rebuild the connectivity with incomplete data)
1674 // * it is useless because zones will be refilled after placing new footprints
1675 m_commit.Push( _( "Update Netlist" ), m_newFootprintsCount ? ZONE_FILL_OP : 0 );
1676
1677 // Update net, netcode and netclass data after commiting the netlist
1679 m_board->GetConnectivity()->RefreshNetcodeMap( m_board );
1680
1681 // Although m_commit will probably also set this, it's not guaranteed, and we need to make
1682 // sure any modification to netclasses gets persisted to project settings through a save.
1683 m_frame->OnModify();
1684 }
1685
1686 if( m_isDryRun )
1687 {
1688 for( const std::pair<const wxString, NETINFO_ITEM*>& addedNet : m_addedNets )
1689 delete addedNet.second;
1690
1691 m_addedNets.clear();
1692 }
1693
1694 // Update the ratsnest
1697
1698 msg.Printf( _( "Total warnings: %d, errors: %d." ), m_warningCount, m_errorCount );
1700
1701 return true;
1702}
const char * name
Definition: DXF_plotter.cpp:62
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:110
#define ZONE_FILL_OP
Definition: board_commit.h:45
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr) override
Add a change of the item aItem of type aChangeType to the change list.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:280
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
void updateComponentClass(FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
bool updateFootprintGroup(FOOTPRINT *aPcbFootprint, COMPONENT *aNetlistComponent)
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)
bool updateGroups(NETLIST &aNetlist)
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:297
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:897
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:1069
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:955
const PAGE_INFO & GetPageSettings() const
Definition: board.h:715
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition: board.cpp:2020
const ZONES & Zones() const
Definition: board.h:342
const GROUPS & Groups() const
The groups must maintain the following invariants.
Definition: board.h:365
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:194
void SynchronizeNetsAndNetClasses(bool aResetTrackAndViaSizes)
Copy NETCLASS info to each NET, based on NET membership in a NETCLASS.
Definition: board.cpp:2159
void RemoveUnusedNets(BOARD_COMMIT *aCommit)
Definition: board.h:902
const FOOTPRINTS & Footprints() const
Definition: board.h:338
const TRACKS & Tracks() const
Definition: board.h:336
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:623
bool IsEmpty() const
Definition: board.h:393
COMPONENT_CLASS_MANAGER & GetComponentClassManager()
Gets the component class manager.
Definition: board.h:1298
bool SynchronizeComponentClasses(const std::unordered_set< wxString > &aNewSheetPaths) const
Copy component class / component class generator information from the project settings.
Definition: board.cpp:2189
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:495
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr Vec Centre() const
Definition: box2.h:97
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr coord_type GetBottom() const
Definition: box2.h:222
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition: commit.h:92
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:108
COMMIT & Added(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:86
COMMIT & Modified(EDA_ITEM *aItem, EDA_ITEM *aCopy, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:118
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition: commit.h:80
int GetStatus(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Returns status of an item.
Definition: commit.cpp:145
COMPONENT_CLASS * GetEffectiveStaticComponentClass(const std::unordered_set< wxString > &classNames)
Gets an effective component class for the given constituent class names.
static wxString GetFullClassNameForConstituents(const std::unordered_set< wxString > &classNames)
Gets the full effective class name for the given set of constituent classes.
void FinishNetlistUpdate()
Cleans up the manager after a board update Must be called after updating the PCB from the netlist.
const COMPONENT_CLASS * GetNoneComponentClass() const
Returns the unassigned component class.
void InitNetlistUpdate()
Prepare the manager for a board update Must be called prior to updating the PCB from the netlist.
A lightweight representation of a component class.
const wxString & GetName() const
Fetches the full name of this component class.
Used to store the component pin name to net name (and pin function) associations stored in a netlist.
Definition: pcb_netlist.h:47
const wxString & GetNetName() const
Definition: pcb_netlist.h:61
bool IsValid() const
Definition: pcb_netlist.h:65
const wxString & GetPinFunction() const
Definition: pcb_netlist.h:62
const wxString & GetPinName() const
Definition: pcb_netlist.h:60
const wxString & GetPinType() const
Definition: pcb_netlist.h:63
Store all of the related footprint information found in a netlist.
Definition: pcb_netlist.h:100
const wxString & GetHumanReadablePath() const
Definition: pcb_netlist.h:191
const COMPONENT_NET & GetNet(unsigned aIndex) const
Definition: pcb_netlist.h:128
const KIID_PATH & GetPath() const
Definition: pcb_netlist.h:166
const wxString & GetReference() const
Definition: pcb_netlist.h:143
const wxString & GetValue() const
Definition: pcb_netlist.h:146
const nlohmann::ordered_map< wxString, wxString > & GetFields() const
Definition: pcb_netlist.h:152
const std::map< wxString, wxString > & GetProperties() const
Definition: pcb_netlist.h:158
NETLIST_GROUP * GetGroup() const
Definition: pcb_netlist.h:206
const std::vector< KIID > & GetKIIDs() const
Definition: pcb_netlist.h:168
bool GetDuplicatePadNumbersAreJumpers() const
Definition: pcb_netlist.h:200
const LIB_ID & GetFPID() const
Definition: pcb_netlist.h:161
unsigned GetNetCount() const
Definition: pcb_netlist.h:126
std::unordered_set< wxString > & GetComponentClassNames()
Definition: pcb_netlist.h:198
std::vector< std::set< wxString > > & JumperPadGroups()
Definition: pcb_netlist.h:203
wxString GetName() const
Definition: eda_group.h:51
virtual bool RemoveItem(EDA_ITEM *aItem)=0
Remove item from group.
virtual EDA_ITEM * AsEdaItem()=0
void SetName(const wxString &aName)
Definition: eda_group.h:52
const KIID m_Uuid
Definition: eda_item.h:502
virtual EDA_GROUP * GetParentGroup() const
Definition: eda_item.h:114
virtual void SetParentGroup(EDA_GROUP *aGroup)
Definition: eda_item.h:113
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:111
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:386
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:270
bool GetDuplicatePadNumbersAreJumpers() const
Definition: footprint.h:820
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:2462
EDA_ANGLE GetOrientation() const
Definition: footprint.h:232
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:1133
wxString GetSheetname() const
Definition: footprint.h:271
void SetPath(const KIID_PATH &aPath)
Definition: footprint.h:269
void SetFilters(const wxString &aFilters)
Definition: footprint.h:278
void SetStaticComponentClass(const COMPONENT_CLASS *aClass) const
Sets the component class object pointer for this footprint.
Definition: footprint.cpp:4044
void SetAttributes(int aAttributes)
Definition: footprint.h:296
void SetSheetfile(const wxString &aSheetfile)
Definition: footprint.h:275
EDA_ITEM * Clone() const override
Invoke a function on all children.
Definition: footprint.cpp:2199
std::vector< std::set< wxString > > & JumperPadGroups()
Each jumper pad group is a set of pad numbers that should be treated as internally connected.
Definition: footprint.h:827
void SetDuplicatePadNumbersAreJumpers(bool aEnabled)
Definition: footprint.h:821
bool HasField(const wxString &aFieldName) const
Definition: footprint.cpp:612
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
Definition: footprint.cpp:585
std::deque< PAD * > & Pads()
Definition: footprint.h:211
int GetAttributes() const
Definition: footprint.h:295
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: footprint.h:241
wxString GetSheetfile() const
Definition: footprint.h:274
const LIB_ID & GetFPID() const
Definition: footprint.h:253
void SetReference(const wxString &aReference)
Definition: footprint.h:627
bool IsLocked() const override
Definition: footprint.h:416
void SetValue(const wxString &aValue)
Definition: footprint.h:648
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:1072
wxString GetFilters() const
Definition: footprint.h:277
void SetSheetname(const wxString &aSheetname)
Definition: footprint.h:272
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
Definition: footprint.cpp:630
const wxString & GetValue() const
Definition: footprint.h:643
const COMPONENT_CLASS * GetStaticComponentClass() const
Returns the component class for this footprint.
Definition: footprint.cpp:4050
const wxString & GetReference() const
Definition: footprint.h:621
const KIID_PATH & GetPath() const
Definition: footprint.h:268
VECTOR2I GetPosition() const override
Definition: footprint.h:229
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
Definition: footprint.cpp:1958
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition: pcb_view.cpp:74
wxString AsString() const
Definition: kiid.cpp:356
Definition: kiid.h:49
bool empty() const
Definition: lib_id.h:193
wxString GetUniStringLibId() const
Definition: lib_id.h:148
UTF8 Format() const
Definition: lib_id.cpp:119
Handle the data for a net.
Definition: netinfo.h:56
void SetIsCurrent(bool isCurrent)
Definition: netinfo.h:152
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:381
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
Definition: pcb_netlist.h:274
unsigned GetCount() const
Definition: pcb_netlist.h:295
COMPONENT * GetComponentByPath(const KIID_PATH &aPath)
Return a COMPONENT by aPath.
COMPONENT * GetComponentByReference(const wxString &aReference)
Return a COMPONENT by aReference.
NETLIST_GROUP * GetGroupByUuid(const KIID &aUuid)
Return a NETLIST_GROUP by aUuid.
COMPONENT * GetComponent(unsigned aIndex)
Return the COMPONENT at aIndex.
Definition: pcb_netlist.h:303
static REPORTER & GetInstance()
Definition: reporter.cpp:108
Definition: pad.h:54
const wxString & GetPinFunction() const
Definition: pad.h:147
const VECTOR2D GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition: page_info.h:171
DISPLAY_OPTIONS m_Display
PCBNEW_SETTINGS * GetPcbNewSettings() const
FOOTPRINT * LoadFootprint(const LIB_ID &aFootprintId)
Attempt to load aFootprintId from the footprint library table.
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Return the BOARD_DESIGN_SETTINGS for the open project.
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
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 resetTextContent=true, bool resetClearanceOverrides=true, bool reset3DModels=true, bool *aUpdated=nullptr)
Replace aExisting footprint by aNew footprint using the Existing footprint settings (position,...
void SetName(const wxString &aName)
Definition: pcb_field.h:108
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:53
EDA_ITEM * AsEdaItem() override
Definition: pcb_group.h:57
bool AddItem(EDA_ITEM *aItem) override
Add item to group.
Definition: pcb_group.cpp:81
void StyleFromSettings(const BOARD_DESIGN_SETTINGS &settings) override
Definition: pcb_text.cpp:310
virtual void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_text.h:89
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition: pcb_text.cpp:380
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition: reporter.h:102
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:112
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
wxString wx_str() const
Definition: utf8.cpp:45
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
@ CHT_UNGROUP
Definition: commit.h:46
The common library.
#define _(s)
@ FP_DNP
Definition: footprint.h:88
@ FP_BOARD_ONLY
Definition: footprint.h:84
@ FP_EXCLUDE_FROM_BOM
Definition: footprint.h:83
@ FP_JUST_ADDED
Definition: footprint.h:85
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_Fab
Definition: layer_ids.h:119
@ F_Cu
Definition: layer_ids.h:64
@ B_Fab
Definition: layer_ids.h:118
Class to handle a set of BOARD_ITEMs.
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
wxString UnescapeString(const wxString &aSource)
const double IU_PER_MILS
Definition: base_units.h:77
constexpr int mmToIU(double mm) const
Definition: base_units.h:90
LIB_ID libId
Definition: pcb_netlist.h:89
wxString name
Definition: pcb_netlist.h:87
wxString GetCanonicalFieldName(FIELD_T aFieldType)
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97