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