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