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>
38#include <netinfo.h>
39#include <footprint.h>
40#include <pad.h>
41#include <pcb_group.h>
42#include <pcb_track.h>
43#include <zone.h>
44#include <string_utils.h>
45#include <limits>
46#include <pcbnew_settings.h>
47#include <pcb_edit_frame.h>
50#include <reporter.h>
51#include <wx/log.h>
52
54
55
57 m_frame( aFrame ),
58 m_commit( aFrame ),
59 m_board( aBoard )
60{
62
64 m_isDryRun = false;
66 m_lookupByTimestamp = false;
67 m_transferGroups = false;
69 m_overrideLocks = false;
70 m_updateFields = false;
71 m_removeExtraFields = false;
72
74 m_errorCount = 0;
76}
77
78
82
83
85 REPORTER* aReporter, bool aDryRun )
86{
87 for( NETINFO_ITEM* net : aBoard->GetNetInfo() )
88 {
89 const wxString previous = net->GetNetChain();
90 wxString next = aNetlist.GetNetChainFor( net->GetNetname() );
91
92 if( !previous.IsEmpty() && next.IsEmpty() && aReporter && !aDryRun )
93 {
94 aReporter->Report(
95 wxString::Format(
96 _( "Net chain assignment '%s' on net '%s' cleared by netlist "
97 "update." ),
98 previous, net->GetNetname() ),
100 }
101
102 if( !aDryRun )
103 {
104 net->SetNetChain( next );
105
106 if( previous != next )
107 {
108 for( int i = 0; i < 2; ++i )
109 net->ClearTerminalPad( i );
110 }
111 }
112 }
113}
114
115
116// These functions allow inspection of pad nets during dry runs by keeping a cache of
117// current pad netnames indexed by pad.
118
119void BOARD_NETLIST_UPDATER::cacheNetname( PAD* aPad, const wxString& aNetname )
120{
121 m_padNets[ aPad ] = aNetname;
122}
123
124
126{
127 if( m_isDryRun && m_padNets.count( aPad ) )
128 return m_padNets[ aPad ];
129 else
130 return aPad->GetNetname();
131}
132
133
134void BOARD_NETLIST_UPDATER::cachePinFunction( PAD* aPad, const wxString& aPinFunction )
135{
136 m_padPinFunctions[ aPad ] = aPinFunction;
137}
138
139
141{
142 if( m_isDryRun && m_padPinFunctions.count( aPad ) )
143 return m_padPinFunctions[ aPad ];
144 else
145 return aPad->GetPinFunction();
146}
147
148
150{
151 VECTOR2I bestPosition;
152
153 if( !m_board->IsEmpty() )
154 {
155 // Position new components below any existing board features.
156 BOX2I bbox = m_board->GetBoardEdgesBoundingBox();
157
158 if( bbox.GetWidth() || bbox.GetHeight() )
159 {
160 bestPosition.x = bbox.Centre().x;
161 bestPosition.y = bbox.GetBottom() + pcbIUScale.mmToIU( 10 );
162 }
163 }
164 else
165 {
166 // Position new components in the center of the page when the board is empty.
167 VECTOR2I pageSize = m_board->GetPageSettings().GetSizeIU( pcbIUScale.IU_PER_MILS );
168
169 bestPosition.x = pageSize.x / 2;
170 bestPosition.y = pageSize.y / 2;
171 }
172
173 return bestPosition;
174}
175
176
178{
179 return addNewFootprint( aComponent, aComponent->GetFPID() );
180}
181
182
184{
185 wxString msg;
186
187 if( aFootprintId.empty() )
188 {
189 msg.Printf( _( "Cannot add %s (no footprint assigned)." ),
190 aComponent->GetReference() );
191 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
192 ++m_errorCount;
193 return nullptr;
194 }
195
196 FOOTPRINT* footprint = m_frame->LoadFootprint( aFootprintId );
197
198 if( footprint == nullptr )
199 {
200 msg.Printf( _( "Cannot add %s (footprint '%s' not found)." ),
201 aComponent->GetReference(),
202 EscapeHTML( aFootprintId.Format().wx_str() ) );
203 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
204 ++m_errorCount;
205 return nullptr;
206 }
207
208 footprint->SetStaticComponentClass(
209 m_board->GetComponentClassManager().GetNoneComponentClass() );
210
211 if( m_isDryRun )
212 {
213 msg.Printf( _( "Add %s (footprint '%s')." ),
214 aComponent->GetReference(),
215 EscapeHTML( aFootprintId.Format().wx_str() ) );
216
217 delete footprint;
218 footprint = nullptr;
219 }
220 else
221 {
222 for( PAD* pad : footprint->Pads() )
223 {
224 // Set the pads ratsnest settings to the global settings
225 pad->SetLocalRatsnestVisible( m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest );
226
227 // Pads in the library all have orphaned nets. Replace with Default.
228 pad->SetNetCode( 0 );
229 }
230
231 footprint->SetParent( m_board );
233
234 // This flag is used to prevent connectivity from considering the footprint during its
235 // initial build after the footprint is committed, because we're going to immediately start
236 // a move operation on the footprint and don't want its pads to drive nets onto vias/tracks
237 // it happens to land on at the initial position.
238 footprint->SetAttributes( footprint->GetAttributes() | FP_JUST_ADDED );
239
240 m_addedFootprints.push_back( footprint );
241 m_commit.Add( footprint );
242
243 msg.Printf( _( "Added %s (footprint '%s')." ),
244 aComponent->GetReference(),
245 EscapeHTML( aFootprintId.Format().wx_str() ) );
246 }
247
248 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
250 return footprint;
251}
252
253
255{
256 wxString curClassName, newClassName;
257 COMPONENT_CLASS* newClass = nullptr;
258
259 if( const COMPONENT_CLASS* curClass = aFootprint->GetStaticComponentClass() )
260 curClassName = curClass->GetName();
261
262 // Calculate the new component class
263 if( m_isDryRun )
264 {
266 aNewComponent->GetComponentClassNames() );
267 }
268 else
269 {
270 newClass = m_board->GetComponentClassManager().GetEffectiveStaticComponentClass(
271 aNewComponent->GetComponentClassNames() );
272 newClassName = newClass->GetName();
273 }
274
275 if( curClassName == newClassName )
276 return false;
277
278 // Create a copy for undo if the footprint has not been added during this update
279 FOOTPRINT* copy = nullptr;
280
281 if( !m_isDryRun && !m_commit.GetStatus( aFootprint ) )
282 {
283 copy = static_cast<FOOTPRINT*>( aFootprint->Clone() );
284 copy->SetParentGroup( nullptr );
285 }
286
287 wxString msg;
288
289 if( m_isDryRun )
290 {
291 if( curClassName == wxEmptyString && newClassName != wxEmptyString )
292 {
293 msg.Printf( _( "Change %s component class to '%s'." ),
294 aFootprint->GetReference(),
295 EscapeHTML( newClassName ) );
296 }
297 else if( curClassName != wxEmptyString && newClassName == wxEmptyString )
298 {
299 msg.Printf( _( "Remove %s component class (currently '%s')." ),
300 aFootprint->GetReference(),
301 EscapeHTML( curClassName ) );
302 }
303 else
304 {
305 msg.Printf( _( "Change %s component class from '%s' to '%s'." ),
306 aFootprint->GetReference(),
307 EscapeHTML( curClassName ),
308 EscapeHTML( newClassName ) );
309 }
310 }
311 else
312 {
313 wxASSERT_MSG( newClass != nullptr, "Component class should not be nullptr" );
314
315 aFootprint->SetStaticComponentClass( newClass );
316
317 if( curClassName == wxEmptyString && newClassName != wxEmptyString )
318 {
319 msg.Printf( _( "Changed %s component class to '%s'." ),
320 aFootprint->GetReference(),
321 EscapeHTML( newClassName ) );
322 }
323 else if( curClassName != wxEmptyString && newClassName == wxEmptyString )
324 {
325 msg.Printf( _( "Removed %s component class (was '%s')." ),
326 aFootprint->GetReference(),
327 EscapeHTML( curClassName ) );
328 }
329 else
330 {
331 msg.Printf( _( "Changed %s component class from '%s' to '%s'." ),
332 aFootprint->GetReference(),
333 EscapeHTML( curClassName ),
334 EscapeHTML( newClassName ) );
335 }
336 }
337
338 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
339
340 if( copy )
341 m_commit.Modified( aFootprint, copy );
342
343 return true;
344}
345
346
348 COMPONENT* aNewComponent )
349{
350 wxString msg;
351
352 if( aNewComponent->GetFPID().empty() )
353 {
354 msg.Printf( _( "Cannot update %s (no footprint assigned)." ),
355 aNewComponent->GetReference() );
356 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
357 ++m_errorCount;
358 return nullptr;
359 }
360
361 FOOTPRINT* newFootprint = m_frame->LoadFootprint( aNewComponent->GetFPID() );
362
363 if( newFootprint == nullptr )
364 {
365 msg.Printf( _( "Cannot update %s (footprint '%s' not found)." ),
366 aNewComponent->GetReference(),
367 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
368 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
369 ++m_errorCount;
370 return nullptr;
371 }
372
373 if( m_isDryRun )
374 {
375 if( aFootprint->IsLocked() && !m_overrideLocks )
376 {
377 msg.Printf( _( "Cannot change %s footprint from '%s' to '%s' (footprint is locked)."),
378 aFootprint->GetReference(),
379 EscapeHTML( aFootprint->GetFPID().Format().wx_str() ),
380 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
381 m_reporter->Report( msg, RPT_SEVERITY_WARNING );
383 delete newFootprint;
384 return nullptr;
385 }
386 else
387 {
388 msg.Printf( _( "Change %s footprint from '%s' to '%s'."),
389 aFootprint->GetReference(),
390 EscapeHTML( aFootprint->GetFPID().Format().wx_str() ),
391 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
392 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
394 delete newFootprint;
395 return nullptr;
396 }
397 }
398 else
399 {
400 if( aFootprint->IsLocked() && !m_overrideLocks )
401 {
402 msg.Printf( _( "Could not change %s footprint from '%s' to '%s' (footprint is locked)."),
403 aFootprint->GetReference(),
404 EscapeHTML( aFootprint->GetFPID().Format().wx_str() ),
405 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
406 m_reporter->Report( msg, RPT_SEVERITY_WARNING );
408 delete newFootprint;
409 return nullptr;
410 }
411 else
412 {
413 // Expand the footprint pad layers
414 newFootprint->FixUpPadsForBoard( m_board );
415
416 m_frame->ExchangeFootprint( aFootprint, newFootprint, m_commit, true );
417
418 msg.Printf( _( "Changed %s footprint from '%s' to '%s'."),
419 aFootprint->GetReference(),
420 EscapeHTML( aFootprint->GetFPID().Format().wx_str() ),
421 EscapeHTML( aNewComponent->GetFPID().Format().wx_str() ) );
422 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
424 return newFootprint;
425 }
426 }
427 }
428
429
431{
432 wxString msg;
433
434 const COMPONENT_VARIANT* firstAssociatedVariant = nullptr;
435
436 if( aFootprint->GetFPID() != aNetlistComponent->GetFPID() )
437 {
438 for( const auto& [_, test] : aNetlistComponent->GetVariants() )
439 {
440 if( test.m_fields.count( GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
441 && aFootprint->GetFPIDAsString() == test.m_fields.at( GetCanonicalFieldName( FIELD_T::FOOTPRINT ) ) )
442 {
443 firstAssociatedVariant = &test;
444 break;
445 }
446 }
447 }
448
449 // Create a copy only if the footprint has not been added during this update
450 FOOTPRINT* copy = nullptr;
451
452 if( !m_commit.GetStatus( aFootprint ) )
453 {
454 copy = static_cast<FOOTPRINT*>( aFootprint->Clone() );
455 copy->SetParentGroup( nullptr );
456 }
457
458 bool changed = false;
459
460 // Test for reference designator field change.
461 if( aFootprint->GetReference() != aNetlistComponent->GetReference() )
462 {
463 if( m_isDryRun )
464 {
465 msg.Printf( _( "Change %s reference designator to %s." ),
466 aFootprint->GetReference(),
467 aNetlistComponent->GetReference() );
468 }
469 else
470 {
471 msg.Printf( _( "Changed %s reference designator to %s." ),
472 aFootprint->GetReference(),
473 aNetlistComponent->GetReference() );
474
475 changed = true;
476 aFootprint->SetReference( aNetlistComponent->GetReference() );
477 }
478
479 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
480 }
481
482 // Test for value field change.
483 wxString netlistValue = aNetlistComponent->GetValue();
484
485 if( firstAssociatedVariant != nullptr
486 && firstAssociatedVariant->m_fields.count( GetCanonicalFieldName( FIELD_T::VALUE ) ) )
487 {
488 netlistValue = firstAssociatedVariant->m_fields.at( GetCanonicalFieldName( FIELD_T::VALUE ) );
489 }
490
491 if( aFootprint->GetValue() != netlistValue )
492 {
493 if( m_isDryRun )
494 {
495 msg.Printf( _( "Change %s value from %s to %s." ),
496 aFootprint->GetReference(),
497 EscapeHTML( aFootprint->GetValue() ),
498 EscapeHTML( netlistValue ) );
499 }
500 else
501 {
502 msg.Printf( _( "Changed %s value from %s to %s." ),
503 aFootprint->GetReference(),
504 EscapeHTML( aFootprint->GetValue() ),
505 EscapeHTML( netlistValue ) );
506
507 changed = true;
508 aFootprint->SetValue( netlistValue );
509 }
510
511 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
512 }
513
514 // Test for time stamp change.
515 KIID_PATH new_path = aNetlistComponent->GetPath();
516
517 if( !aNetlistComponent->GetKIIDs().empty() )
518 new_path.push_back( aNetlistComponent->GetKIIDs().front() );
519
520 if( aFootprint->GetPath() != new_path )
521 {
522 if( m_isDryRun )
523 {
524 msg.Printf( _( "Update %s symbol association from %s to %s." ),
525 aFootprint->GetReference(),
526 EscapeHTML( aFootprint->GetPath().AsString() ),
527 EscapeHTML( new_path.AsString() ) );
528 }
529 else
530 {
531 msg.Printf( _( "Updated %s symbol association from %s to %s." ),
532 aFootprint->GetReference(),
533 EscapeHTML( aFootprint->GetPath().AsString() ),
534 EscapeHTML( new_path.AsString() ) );
535
536 changed = true;
537 aFootprint->SetPath( new_path );
538 }
539
540 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
541 }
542
543 nlohmann::ordered_map<wxString, wxString> fpFieldsAsMap;
544
545 for( PCB_FIELD* field : aFootprint->GetFields() )
546 {
547 // These fields are individually checked above
548 if( field->IsReference() || field->IsValue() || field->IsComponentClass() )
549 {
550 continue;
551 }
552
553 fpFieldsAsMap[field->GetName()] = field->GetText();
554 }
555
556 // Remove the ref/value/footprint fields that are individually handled
557 nlohmann::ordered_map<wxString, wxString> compFields = aNetlistComponent->GetFields();
558 compFields.erase( GetCanonicalFieldName( FIELD_T::REFERENCE ) );
559 compFields.erase( GetCanonicalFieldName( FIELD_T::VALUE ) );
560 compFields.erase( GetCanonicalFieldName( FIELD_T::FOOTPRINT ) );
561
562 // Remove any component class fields - these are not editable in the pcb editor
563 compFields.erase( wxT( "Component Class" ) );
564
565 if( firstAssociatedVariant != nullptr )
566 {
567 for( const auto& [name, value] : firstAssociatedVariant->m_fields )
568 compFields[name] = value;
569 }
570
571 // Fields are stored as an ordered map, but we don't (yet) support reordering the footprint fields to
572 // match the symbol, so we manually check the fields in the order they are stored in the symbol.
573 bool same = true;
574 bool remove_only = true;
575
576 for( const auto& [name, value] : compFields )
577 {
578 if( fpFieldsAsMap.count( name ) == 0 || fpFieldsAsMap[name] != value )
579 {
580 same = false;
581 remove_only = false;
582 break;
583 }
584 }
585
586 for( const auto& [name, value] : fpFieldsAsMap )
587 {
588 if( compFields.count( name ) == 0 )
589 {
590 same = false;
591 break;
592 }
593 }
594
595 if( !same )
596 {
597 if( m_isDryRun )
598 {
599 if( m_updateFields && ( !remove_only || m_removeExtraFields ) )
600 {
601 msg.Printf( _( "Update %s fields." ), aFootprint->GetReference() );
602 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
603 }
604
605 // Remove fields that aren't present in the symbol
606 for( PCB_FIELD* field : aFootprint->GetFields() )
607 {
608 if( field->IsMandatory() )
609 continue;
610
611 if( compFields.count( field->GetName() ) == 0 )
612 {
614 {
615 msg.Printf( _( "Remove %s footprint fields not in symbol." ), aFootprint->GetReference() );
616 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
617 }
618
619 break;
620 }
621 }
622 }
623 else
624 {
625 if( m_updateFields && ( !remove_only || m_removeExtraFields ) )
626 {
627 msg.Printf( _( "Updated %s fields." ), aFootprint->GetReference() );
628 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
629
630 changed = true;
631
632 // Add or change field value
633 for( auto& [name, value] : compFields )
634 {
635 if( aFootprint->HasField( name ) )
636 {
637 aFootprint->GetField( name )->SetText( value );
638 }
639 else
640 {
641 PCB_FIELD* newField = new PCB_FIELD( aFootprint, FIELD_T::USER );
642 aFootprint->Add( newField );
643
644 newField->SetName( name );
645 newField->SetText( value );
646 newField->SetVisible( false );
647 newField->SetLayer( aFootprint->GetLayer() == F_Cu ? F_Fab : B_Fab );
648
649 // Give the relative position (0,0) in footprint
650 newField->SetPosition( aFootprint->GetPosition() );
651 // Give the footprint orientation
652 newField->Rotate( aFootprint->GetPosition(), aFootprint->GetOrientation() );
653
654 if( m_frame )
655 newField->StyleFromSettings( m_frame->GetDesignSettings(), true );
656 }
657 }
658 }
659
661 {
662 bool warned = false;
663
664 std::vector<PCB_FIELD*> fieldList;
665 aFootprint->GetFields( fieldList, false );
666
667 for( PCB_FIELD* field : fieldList )
668 {
669 if( field->IsMandatory() )
670 continue;
671
672 if( compFields.count( field->GetName() ) == 0 )
673 {
674 if( !warned )
675 {
676 warned = true;
677 msg.Printf( _( "Removed %s footprint fields not in symbol." ), aFootprint->GetReference() );
678 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
679 }
680
681 aFootprint->Remove( field );
682
683 if( m_frame )
684 m_frame->GetCanvas()->GetView()->Remove( field );
685
686 delete field;
687 }
688 }
689 }
690 }
691 }
692
693 wxString sheetname;
694 wxString sheetfile;
695 wxString fpFilters;
696
697 wxString humanSheetPath = aNetlistComponent->GetHumanReadablePath();
698
699 if( !humanSheetPath.empty() )
700 sheetname = humanSheetPath;
701 else if( aNetlistComponent->GetProperties().count( wxT( "Sheetname" ) ) > 0 )
702 sheetname = aNetlistComponent->GetProperties().at( wxT( "Sheetname" ) );
703
704 if( aNetlistComponent->GetProperties().count( wxT( "Sheetfile" ) ) > 0 )
705 sheetfile = aNetlistComponent->GetProperties().at( wxT( "Sheetfile" ) );
706
707 if( aNetlistComponent->GetProperties().count( wxT( "ki_fp_filters" ) ) > 0 )
708 fpFilters = aNetlistComponent->GetProperties().at( wxT( "ki_fp_filters" ) );
709
710 if( sheetname != aFootprint->GetSheetname() )
711 {
712 if( m_isDryRun )
713 {
714 msg.Printf( _( "Update %s sheetname to '%s'." ),
715 aFootprint->GetReference(),
716 EscapeHTML( sheetname ) );
717 }
718 else
719 {
720 aFootprint->SetSheetname( sheetname );
721 msg.Printf( _( "Updated %s sheetname to '%s'." ),
722 aFootprint->GetReference(),
723 EscapeHTML( sheetname ) );
724 }
725
726 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
727 }
728
729 if( sheetfile != aFootprint->GetSheetfile() )
730 {
731 if( m_isDryRun )
732 {
733 msg.Printf( _( "Update %s sheetfile to '%s'." ),
734 aFootprint->GetReference(),
735 EscapeHTML( sheetfile ) );
736 }
737 else
738 {
739 aFootprint->SetSheetfile( sheetfile );
740 msg.Printf( _( "Updated %s sheetfile to '%s'." ),
741 aFootprint->GetReference(),
742 EscapeHTML( sheetfile ) );
743 }
744
745 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
746 }
747
748 if( fpFilters != aFootprint->GetFilters() )
749 {
750 if( m_isDryRun )
751 {
752 msg.Printf( _( "Update %s footprint filters to '%s'." ),
753 aFootprint->GetReference(),
754 EscapeHTML( fpFilters ) );
755 }
756 else
757 {
758 aFootprint->SetFilters( fpFilters );
759 msg.Printf( _( "Updated %s footprint filters to '%s'." ),
760 aFootprint->GetReference(),
761 EscapeHTML( fpFilters ) );
762 }
763
764 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
765 }
766
767 bool netlistExcludeFromBOM = aNetlistComponent->GetProperties().count( wxT( "exclude_from_bom" ) ) > 0;
768
769 if( firstAssociatedVariant != nullptr && firstAssociatedVariant->m_hasExcludedFromBOM )
770 netlistExcludeFromBOM = firstAssociatedVariant->m_excludedFromBOM;
771
772 if( m_updateFields && netlistExcludeFromBOM != ( ( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_BOM ) > 0 ) )
773 {
774 if( m_isDryRun )
775 {
776 if( netlistExcludeFromBOM )
777 msg.Printf( _( "Add %s 'exclude from BOM' fabrication attribute." ), aFootprint->GetReference() );
778 else
779 msg.Printf( _( "Remove %s 'exclude from BOM' fabrication attribute." ), aFootprint->GetReference() );
780 }
781 else
782 {
783 int attributes = aFootprint->GetAttributes();
784
785 if( netlistExcludeFromBOM )
786 {
787 attributes |= FP_EXCLUDE_FROM_BOM;
788 msg.Printf( _( "Added %s 'exclude from BOM' fabrication attribute." ), aFootprint->GetReference() );
789 }
790 else
791 {
792 attributes &= ~FP_EXCLUDE_FROM_BOM;
793 msg.Printf( _( "Removed %s 'exclude from BOM' fabrication attribute." ), aFootprint->GetReference() );
794 }
795
796 changed = true;
797 aFootprint->SetAttributes( attributes );
798 }
799
800 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
801 }
802
803 bool netlistDNP = aNetlistComponent->GetProperties().count( wxT( "dnp" ) ) > 0;
804
805 if( firstAssociatedVariant != nullptr && firstAssociatedVariant->m_hasDnp )
806 netlistDNP = firstAssociatedVariant->m_dnp;
807
808 if( m_updateFields && netlistDNP != ( ( aFootprint->GetAttributes() & FP_DNP ) > 0 ) )
809 {
810 if( m_isDryRun )
811 {
812 if( netlistDNP )
813 msg.Printf( _( "Add %s 'Do not place' fabrication attribute." ), aFootprint->GetReference() );
814 else
815 msg.Printf( _( "Remove %s 'Do not place' fabrication attribute." ), aFootprint->GetReference() );
816 }
817 else
818 {
819 int attributes = aFootprint->GetAttributes();
820
821 if( netlistDNP )
822 {
823 attributes |= FP_DNP;
824 msg.Printf( _( "Added %s 'Do not place' fabrication attribute." ), aFootprint->GetReference() );
825 }
826 else
827 {
828 attributes &= ~FP_DNP;
829 msg.Printf( _( "Removed %s 'Do not place' fabrication attribute." ), aFootprint->GetReference() );
830 }
831
832 changed = true;
833 aFootprint->SetAttributes( attributes );
834 }
835
836 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
837 }
838
839 bool netlistExcludeFromPosFiles = aNetlistComponent->GetProperties().count( wxT( "exclude_from_pos_files" ) ) > 0;
840
841 if( firstAssociatedVariant != nullptr && firstAssociatedVariant->m_hasExcludedFromPosFiles )
842 netlistExcludeFromPosFiles = firstAssociatedVariant->m_excludedFromPosFiles;
843
845 && netlistExcludeFromPosFiles != ( ( aFootprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES ) > 0 ) )
846 {
847 if( m_isDryRun )
848 {
849 if( netlistExcludeFromPosFiles )
850 {
851 msg.Printf( _( "Add %s 'exclude from position files' fabrication attribute." ),
852 aFootprint->GetReference() );
853 }
854 else
855 {
856 msg.Printf( _( "Remove %s 'exclude from position files' fabrication attribute." ),
857 aFootprint->GetReference() );
858 }
859 }
860 else
861 {
862 int attributes = aFootprint->GetAttributes();
863
864 if( netlistExcludeFromPosFiles )
865 {
866 attributes |= FP_EXCLUDE_FROM_POS_FILES;
867 msg.Printf( _( "Added %s 'exclude from position files' fabrication attribute." ),
868 aFootprint->GetReference() );
869 }
870 else
871 {
872 attributes &= ~FP_EXCLUDE_FROM_POS_FILES;
873 msg.Printf( _( "Removed %s 'exclude from position files' fabrication attribute." ),
874 aFootprint->GetReference() );
875 }
876
877 changed = true;
878 aFootprint->SetAttributes( attributes );
879 }
880
881 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
882 }
883
885 && aNetlistComponent->GetDuplicatePadNumbersAreJumpers() != aFootprint->GetDuplicatePadNumbersAreJumpers() )
886 {
887 bool value = aNetlistComponent->GetDuplicatePadNumbersAreJumpers();
888
889 if( !m_isDryRun )
890 {
891 changed = true;
892 aFootprint->SetDuplicatePadNumbersAreJumpers( value );
893
894 if( value )
895 {
896 msg.Printf( _( "Added %s 'duplicate pad numbers are jumpers' attribute." ),
897 aFootprint->GetReference() );
898 }
899 else
900 {
901 msg.Printf( _( "Removed %s 'duplicate pad numbers are jumpers' attribute." ),
902 aFootprint->GetReference() );
903 }
904 }
905 else
906 {
907 if( value )
908 {
909 msg.Printf( _( "Add %s 'duplicate pad numbers are jumpers' attribute." ),
910 aFootprint->GetReference() );
911 }
912 else
913 {
914 msg.Printf( _( "Remove %s 'duplicate pad numbers are jumpers' attribute." ),
915 aFootprint->GetReference() );
916 }
917 }
918
919 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
920 }
921
922 if( m_updateFields && aNetlistComponent->JumperPadGroups() != aFootprint->JumperPadGroups() )
923 {
924 if( !m_isDryRun )
925 {
926 changed = true;
927 aFootprint->JumperPadGroups() = aNetlistComponent->JumperPadGroups();
928 msg.Printf( _( "Updated %s jumper pad groups" ), aFootprint->GetReference() );
929 }
930 else
931 {
932 msg.Printf( _( "Update %s jumper pad groups" ), aFootprint->GetReference() );
933 }
934
935 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
936 }
937
938 if( changed && copy )
939 m_commit.Modified( aFootprint, copy );
940 else
941 delete copy;
942
943 return true;
944}
945
946
948 COMPONENT* aNetlistComponent )
949{
950 if( !m_transferGroups )
951 return false;
952
953 wxString msg;
954
955 // Create a copy only if the footprint has not been added during this update
956 FOOTPRINT* copy = nullptr;
957
958 if( !m_commit.GetStatus( aPcbFootprint ) )
959 {
960 copy = static_cast<FOOTPRINT*>( aPcbFootprint->Clone() );
961 copy->SetParentGroup( nullptr );
962 }
963
964 bool changed = false;
965
966 // These hold the info for group and group KIID coming from the netlist
967 // newGroup may point to an existing group on the board if we find an
968 // incoming group UUID that matches an existing group
969 PCB_GROUP* newGroup = nullptr;
970 KIID newGroupKIID = aNetlistComponent->GetGroup() ? aNetlistComponent->GetGroup()->uuid : 0;
971
972 PCB_GROUP* existingGroup = static_cast<PCB_GROUP*>( aPcbFootprint->GetParentGroup() );
973 KIID existingGroupKIID = existingGroup ? existingGroup->m_Uuid : 0;
974
975 // Find existing group based on matching UUIDs
976 auto it = std::find_if( m_board->Groups().begin(), m_board->Groups().end(),
977 [&](PCB_GROUP* group) {
978 return group->m_Uuid == newGroupKIID;
979 });
980
981 // If we find a group with the same UUID, use it
982 if( it != m_board->Groups().end() )
983 newGroup = *it;
984
985 // No changes, nothing to do
986 if( newGroupKIID == existingGroupKIID )
987 return changed;
988
989 // Remove from existing group
990 if( existingGroupKIID != 0 )
991 {
992 if( m_isDryRun )
993 {
994 msg.Printf( _( "Remove %s from group '%s'." ),
995 aPcbFootprint->GetReference(),
996 EscapeHTML( existingGroup->GetName() ) );
997 }
998 else
999 {
1000 msg.Printf( _( "Removed %s from group '%s'." ),
1001 aPcbFootprint->GetReference(),
1002 EscapeHTML( existingGroup->GetName() ) );
1003
1004 changed = true;
1005 m_commit.Modify( existingGroup, nullptr, RECURSE_MODE::NO_RECURSE );
1006 existingGroup->RemoveItem( aPcbFootprint );
1007
1008 if( existingGroup->GetItems().size() < 2 )
1009 {
1010 existingGroup->RemoveAll();
1011 m_commit.Remove( existingGroup );
1012 }
1013 }
1014
1015 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1016 }
1017
1018 // Add to new group
1019 if( newGroupKIID != 0 )
1020 {
1021 if( m_isDryRun )
1022 {
1023 msg.Printf( _( "Add %s to group '%s'." ),
1024 aPcbFootprint->GetReference(),
1025 EscapeHTML( aNetlistComponent->GetGroup()->name ) );
1026 }
1027 else
1028 {
1029 msg.Printf( _( "Added %s to group '%s'." ),
1030 aPcbFootprint->GetReference(),
1031 EscapeHTML( aNetlistComponent->GetGroup()->name ) );
1032
1033 changed = true;
1034
1035 if( newGroup == nullptr )
1036 {
1037 newGroup = new PCB_GROUP( m_board );
1038 newGroup->SetUuid( newGroupKIID );
1039 newGroup->SetName( aNetlistComponent->GetGroup()->name );
1040
1041 // Add the group to the board manually so we can find it by checking
1042 // board groups for later footprints that are checking for existing groups
1043 m_board->Add( newGroup );
1044 m_commit.Added( newGroup );
1045 m_addedGroups.push_back( newGroup );
1046 }
1047 else
1048 {
1049 m_commit.Modify( newGroup->AsEdaItem(), nullptr, RECURSE_MODE::NO_RECURSE );
1050 }
1051
1052 newGroup->AddItem( aPcbFootprint );
1053 }
1054
1055 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1056 }
1057
1058 if( changed && copy )
1059 m_commit.Modified( aPcbFootprint, copy );
1060 else if( copy )
1061 delete copy;
1062
1063 return changed;
1064}
1065
1066
1068 COMPONENT* aNewComponent )
1069{
1070 wxString msg;
1071
1072 // Create a copy only if the footprint has not been added during this update
1073 FOOTPRINT* copy = nullptr;
1074
1075 if( !m_isDryRun && !m_commit.GetStatus( aFootprint ) )
1076 {
1077 copy = static_cast<FOOTPRINT*>( aFootprint->Clone() );
1078 copy->SetParentGroup( nullptr );
1079 }
1080
1081 bool changed = false;
1082
1083 // At this point, the component footprint is updated. Now update the nets.
1084 std::deque<PAD*> pads = aFootprint->Pads();
1085 std::set<wxString> padNetnames;
1086
1087 std::sort( pads.begin(), pads.end(),
1088 []( PAD* a, PAD* b )
1089 {
1090 return a->m_Uuid < b->m_Uuid;
1091 } );
1092
1093 for( PAD* pad : pads )
1094 {
1095 const COMPONENT_NET& net = aNewComponent->GetNet( pad->GetNumber() );
1096
1097 wxLogTrace( wxT( "NETLIST_UPDATE" ),
1098 wxT( "Processing pad %s of component %s" ),
1099 pad->GetNumber(),
1100 aNewComponent->GetReference() );
1101
1102 wxString pinFunction;
1103 wxString pinType;
1104
1105 if( net.IsValid() ) // i.e. the pad has a name
1106 {
1107 wxLogTrace( wxT( "NETLIST_UPDATE" ),
1108 wxT( " Found valid net: %s" ),
1109 net.GetNetName() );
1110 pinFunction = net.GetPinFunction();
1111 pinType = net.GetPinType();
1112 }
1113 else
1114 {
1115 wxLogTrace( wxT( "NETLIST_UPDATE" ),
1116 wxT( " No net found for pad %s" ),
1117 pad->GetNumber() );
1118 }
1119
1120 if( !m_isDryRun )
1121 {
1122 if( pad->GetPinFunction() != pinFunction )
1123 {
1124 changed = true;
1125 pad->SetPinFunction( pinFunction );
1126 }
1127
1128 if( pad->GetPinType() != pinType )
1129 {
1130 changed = true;
1131 pad->SetPinType( pinType );
1132 }
1133 }
1134 else
1135 {
1136 cachePinFunction( pad, pinFunction );
1137 }
1138
1139 // Test if new footprint pad has no net (pads not on copper layers have no net).
1140 if( !net.IsValid() || !pad->IsOnCopperLayer() )
1141 {
1142 if( !pad->GetNetname().IsEmpty() )
1143 {
1144 if( m_isDryRun )
1145 {
1146 msg.Printf( _( "Disconnect %s pin %s." ),
1147 aFootprint->GetReference(),
1148 EscapeHTML( pad->GetNumber() ) );
1149 }
1150 else
1151 {
1152 msg.Printf( _( "Disconnected %s pin %s." ),
1153 aFootprint->GetReference(),
1154 EscapeHTML( pad->GetNumber() ) );
1155 }
1156
1157 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1158 }
1159 else if( pad->IsOnCopperLayer() && !pad->GetNumber().IsEmpty() )
1160 {
1161 // pad is connectable but has no net found in netlist
1162 msg.Printf( _( "No net found for component %s pad %s (no pin %s in symbol)." ),
1163 aFootprint->GetReference(),
1164 EscapeHTML( pad->GetNumber() ),
1165 EscapeHTML( pad->GetNumber() ) );
1166 m_reporter->Report( msg, RPT_SEVERITY_WARNING);
1168 }
1169
1170 if( !m_isDryRun )
1171 {
1172 changed = true;
1173 pad->SetNetCode( NETINFO_LIST::UNCONNECTED );
1174
1175 // If the pad has no net from netlist (i.e. not in netlist
1176 // it cannot have a pin function
1177 if( pad->GetNetname().IsEmpty() )
1178 pad->SetPinFunction( wxEmptyString );
1179
1180 }
1181 else
1182 {
1183 cacheNetname( pad, wxEmptyString );
1184 }
1185 }
1186 else // New footprint pad has a net.
1187 {
1188 wxString netName = net.GetNetName();
1189
1190 if( pad->IsNoConnectPad() )
1191 {
1192 netName = wxString::Format( wxS( "%s" ), net.GetNetName() );
1193
1194 for( int jj = 1; !padNetnames.insert( netName ).second
1195 || ( netName != net.GetNetName() && m_schematicNetNames.count( netName ) );
1196 jj++ )
1197 {
1198 netName = wxString::Format( wxS( "%s_%d" ), net.GetNetName(), jj );
1199 }
1200 }
1201
1202 NETINFO_ITEM* netinfo = m_board->FindNet( netName );
1203
1204 if( netinfo && !m_isDryRun )
1205 netinfo->SetIsCurrent( true );
1206
1207 if( pad->GetNetname() != netName )
1208 {
1209
1210 if( netinfo == nullptr )
1211 {
1212 // It might be a new net that has not been added to the board yet
1213 if( m_addedNets.count( netName ) )
1214 netinfo = m_addedNets[ netName ];
1215 }
1216
1217 if( netinfo == nullptr )
1218 {
1219 netinfo = new NETINFO_ITEM( m_board, netName );
1220
1221 // It is a new net, we have to add it
1222 if( !m_isDryRun )
1223 {
1224 changed = true;
1225 m_commit.Add( netinfo );
1226 }
1227
1228 m_addedNets[netName] = netinfo;
1229 msg.Printf( _( "Add net %s." ),
1230 EscapeHTML( UnescapeString( netName ) ) );
1231 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1232 }
1233
1234 if( !pad->GetNetname().IsEmpty() )
1235 {
1236 m_oldToNewNets[ pad->GetNetname() ] = netName;
1237
1238 if( m_isDryRun )
1239 {
1240 msg.Printf( _( "Reconnect %s pin %s from %s to %s."),
1241 aFootprint->GetReference(),
1242 EscapeHTML( pad->GetNumber() ),
1243 EscapeHTML( UnescapeString( pad->GetNetname() ) ),
1244 EscapeHTML( UnescapeString( netName ) ) );
1245 }
1246 else
1247 {
1248 msg.Printf( _( "Reconnected %s pin %s from %s to %s."),
1249 aFootprint->GetReference(),
1250 EscapeHTML( pad->GetNumber() ),
1251 EscapeHTML( UnescapeString( pad->GetNetname() ) ),
1252 EscapeHTML( UnescapeString( netName ) ) );
1253 }
1254 }
1255 else
1256 {
1257 if( m_isDryRun )
1258 {
1259 msg.Printf( _( "Connect %s pin %s to %s."),
1260 aFootprint->GetReference(),
1261 EscapeHTML( pad->GetNumber() ),
1262 EscapeHTML( UnescapeString( netName ) ) );
1263 }
1264 else
1265 {
1266 msg.Printf( _( "Connected %s pin %s to %s."),
1267 aFootprint->GetReference(),
1268 EscapeHTML( pad->GetNumber() ),
1269 EscapeHTML( UnescapeString( netName ) ) );
1270 }
1271 }
1272
1273 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1274
1275 if( !m_isDryRun )
1276 {
1277 changed = true;
1278 pad->SetNet( netinfo );
1279 }
1280 else
1281 {
1282 cacheNetname( pad, netName );
1283 }
1284 }
1285 }
1286 }
1287
1288 if( changed && copy )
1289 m_commit.Modified( aFootprint, copy );
1290 else if( copy )
1291 delete copy;
1292
1293 return true;
1294}
1295
1296
1298{
1299 // Build the footprint-side representation from the netlist component
1300 std::vector<FOOTPRINT::FP_UNIT_INFO> newUnits;
1301
1302 for( const COMPONENT::UNIT_INFO& u : aNewComponent->GetUnitInfo() )
1303 newUnits.push_back( { u.m_unitName, u.m_pins } );
1304
1305 const std::vector<FOOTPRINT::FP_UNIT_INFO>& curUnits = aFootprint->GetUnitInfo();
1306
1307 auto unitsEqual = []( const std::vector<FOOTPRINT::FP_UNIT_INFO>& a,
1308 const std::vector<FOOTPRINT::FP_UNIT_INFO>& b )
1309 {
1310 if( a.size() != b.size() )
1311 return false;
1312
1313 for( size_t i = 0; i < a.size(); ++i )
1314 {
1315 if( a[i].m_unitName != b[i].m_unitName )
1316 return false;
1317
1318 if( a[i].m_pins != b[i].m_pins )
1319 return false;
1320 }
1321
1322 return true;
1323 };
1324
1325 if( unitsEqual( curUnits, newUnits ) )
1326 return false;
1327
1328 wxString msg;
1329
1330 if( m_isDryRun )
1331 {
1332 msg.Printf( _( "Update %s unit metadata." ), aFootprint->GetReference() );
1333 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1334 return false; // no actual change on board during dry run
1335 }
1336
1337 // Create a copy only if the footprint has not been added during this update
1338 FOOTPRINT* copy = nullptr;
1339
1340 if( !m_commit.GetStatus( aFootprint ) )
1341 {
1342 copy = static_cast<FOOTPRINT*>( aFootprint->Clone() );
1343 copy->SetParentGroup( nullptr );
1344 }
1345
1346 aFootprint->SetUnitInfo( newUnits );
1347
1348 msg.Printf( _( "Updated %s unit metadata." ), aFootprint->GetReference() );
1349 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1350
1351 if( copy )
1352 m_commit.Modified( aFootprint, copy );
1353
1354 return true;
1355}
1356
1357
1359 const std::vector<FOOTPRINT*>& aFootprints,
1360 const LIB_ID& aBaseFpid )
1361{
1362 wxString msg;
1363 const auto& variants = aComponent->GetVariants();
1364
1365 if( aBaseFpid.empty() )
1366 return;
1367
1368 const wxString footprintFieldName = GetCanonicalFieldName( FIELD_T::FOOTPRINT );
1369
1370 struct VARIANT_INFO
1371 {
1372 wxString name;
1373 const COMPONENT_VARIANT* variant;
1374 LIB_ID variantFPID;
1375 };
1376
1377 std::vector<VARIANT_INFO> variantInfo;
1378 variantInfo.reserve( variants.size() );
1379
1380 for( const auto& [variantName, variant] : variants )
1381 {
1382 LIB_ID variantFPID = aBaseFpid;
1383
1384 auto fieldIt = variant.m_fields.find( footprintFieldName );
1385
1386 if( fieldIt != variant.m_fields.end() && !fieldIt->second.IsEmpty() )
1387 {
1388 LIB_ID parsedId;
1389
1390 if( parsedId.Parse( fieldIt->second, true ) >= 0 )
1391 {
1392 msg.Printf( _( "Invalid footprint ID '%s' for variant '%s' on %s." ),
1393 EscapeHTML( fieldIt->second ),
1394 variantName,
1395 aComponent->GetReference() );
1396 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1397 ++m_errorCount;
1398 }
1399 else
1400 {
1401 variantFPID = parsedId;
1402 }
1403 }
1404
1405 variantInfo.push_back( { variantName, &variant, variantFPID } );
1406 }
1407
1408 for( FOOTPRINT* footprint : aFootprints )
1409 {
1410 if( !footprint )
1411 continue;
1412
1413 FOOTPRINT* copy = nullptr;
1414
1415 if( !m_isDryRun && !m_commit.GetStatus( footprint ) )
1416 {
1417 copy = static_cast<FOOTPRINT*>( footprint->Clone() );
1418 copy->SetParentGroup( nullptr );
1419 }
1420
1421 bool changed = false;
1422
1423 auto printAttributeMessage =
1424 [&]( bool add, const wxString& attrName, const wxString& variantName )
1425 {
1426 if( m_isDryRun )
1427 {
1428 if( aFootprints.size() > 1 )
1429 {
1430 msg.Printf( add ? _( "Add %s '%s' attribute to variant %s (footprint %s)." )
1431 : _( "Remove %s '%s' attribute from variant %s (footprint %s)." ),
1432 footprint->GetReference(),
1433 attrName,
1434 variantName,
1435 footprint->GetFPIDAsString() );
1436 }
1437 else
1438 {
1439 msg.Printf( add ? _( "Add %s '%s' attribute to variant %s." )
1440 : _( "Remove %s '%s' attribute from variant %s." ),
1441 footprint->GetReference(),
1442 attrName,
1443 variantName );
1444 }
1445 }
1446 else
1447 {
1448 if( aFootprints.size() > 1 )
1449 {
1450 msg.Printf( add ? _( "Added %s '%s' attribute to variant %s (footprint %s)." )
1451 : _( "Removed %s '%s' attribute from variant %s (footprint %s)." ),
1452 footprint->GetReference(),
1453 attrName,
1454 variantName,
1455 footprint->GetFPIDAsString() );
1456 }
1457 else
1458 {
1459 msg.Printf( add ? _( "Added %s '%s' attribute to variant %s." )
1460 : _( "Removed %s '%s' attribute from variant %s." ),
1461 footprint->GetReference(),
1462 attrName,
1463 variantName );
1464 }
1465 }
1466 };
1467
1468 std::set<wxString> excessVariants;
1469
1470 for( const auto& [variantName, _] : footprint->GetVariants() )
1471 excessVariants.insert( variantName );
1472
1473 for( const VARIANT_INFO& info : variantInfo )
1474 {
1475 const COMPONENT_VARIANT& variant = *info.variant;
1476
1477 // During dry run, just read current state. During actual run, create variant if needed.
1478 const FOOTPRINT_VARIANT* currentVariant = footprint->GetVariant( info.name );
1479
1480 // Check if this footprint is the active one for this variant
1481 bool isAssociatedFootprint = ( footprint->GetFPID() == info.variantFPID );
1482
1483 // If this footprint is not active for this variant, it doesn't need variant info for it.
1484 // Otherwise, apply explicit overrides from schematic, or reset to base footprint value.
1485
1486 if( !isAssociatedFootprint )
1487 continue;
1488
1489 excessVariants.erase( info.name );
1490 bool targetDnp = variant.m_hasDnp ? variant.m_dnp : footprint->IsDNP();
1491 bool currentDnp = currentVariant ? currentVariant->GetDNP() : footprint->IsDNP();
1492
1493 if( currentDnp != targetDnp )
1494 {
1495 printAttributeMessage( targetDnp, _( "Do not place" ), info.name );
1496
1497 if( !m_isDryRun )
1498 {
1499 if( FOOTPRINT_VARIANT* fpVariant = footprint->AddVariant( info.name ) )
1500 fpVariant->SetDNP( targetDnp );
1501 }
1502
1503 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1504 changed = true;
1505 }
1506
1507 bool targetExcludedFromBOM = variant.m_hasExcludedFromBOM ? variant.m_excludedFromBOM
1508 : footprint->IsExcludedFromBOM();
1509 bool currentExcludedFromBOM = currentVariant ? currentVariant->GetExcludedFromBOM()
1510 : footprint->IsExcludedFromBOM();
1511
1512 if( currentExcludedFromBOM != targetExcludedFromBOM )
1513 {
1514 printAttributeMessage( targetExcludedFromBOM, _( "exclude from BOM" ), info.name );
1515
1516 if( !m_isDryRun )
1517 {
1518 if( FOOTPRINT_VARIANT* fpVariant = footprint->AddVariant( info.name ) )
1519 fpVariant->SetExcludedFromBOM( targetExcludedFromBOM );
1520 }
1521
1522 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1523 changed = true;
1524 }
1525
1526 bool targetExcludedFromPosFiles = variant.m_hasExcludedFromPosFiles ? variant.m_excludedFromPosFiles
1527 : footprint->IsExcludedFromPosFiles();
1528 bool currentExcludedFromPosFiles = currentVariant ? currentVariant->GetExcludedFromPosFiles()
1529 : footprint->IsExcludedFromPosFiles();
1530
1531 if( currentExcludedFromPosFiles != targetExcludedFromPosFiles )
1532 {
1533 printAttributeMessage( targetExcludedFromPosFiles, _( "exclude from position files" ), info.name );
1534
1535 if( !m_isDryRun )
1536 {
1537 if( FOOTPRINT_VARIANT* fpVariant = footprint->AddVariant( info.name ) )
1538 fpVariant->SetExcludedFromPosFiles( targetExcludedFromPosFiles );
1539 }
1540
1541 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1542 changed = true;
1543 }
1544
1545 for( const auto& [fieldName, fieldValue] : variant.m_fields )
1546 {
1547 if( fieldName.CmpNoCase( footprintFieldName ) == 0 )
1548 continue;
1549
1550 bool hasCurrentValue = currentVariant && currentVariant->HasFieldValue( fieldName );
1551 wxString currentValue = hasCurrentValue ? currentVariant->GetFieldValue( fieldName ) : wxString();
1552
1553 if( currentValue != fieldValue )
1554 {
1555 if( m_isDryRun )
1556 {
1557 if( aFootprints.size() > 1 )
1558 {
1559 msg.Printf( _( "Change %s field '%s' to '%s' on variant %s (footprint %s)." ),
1560 footprint->GetReference(),
1561 fieldName,
1562 fieldValue,
1563 info.name,
1564 footprint->GetFPIDAsString() );
1565 }
1566 else
1567 {
1568 msg.Printf( _( "Change %s field '%s' to '%s' on variant %s." ),
1569 footprint->GetReference(),
1570 fieldName,
1571 fieldValue,
1572 info.name );
1573 }
1574 }
1575 else
1576 {
1577 if( aFootprints.size() > 1 )
1578 {
1579 msg.Printf( _( "Changed %s field '%s' to '%s' on variant %s (footprint %s)." ),
1580 footprint->GetReference(),
1581 fieldName,
1582 fieldValue,
1583 info.name,
1584 footprint->GetFPIDAsString() );
1585 }
1586 else
1587 {
1588 msg.Printf( _( "Changed %s field '%s' to '%s' on variant %s." ),
1589 footprint->GetReference(),
1590 fieldName,
1591 fieldValue,
1592 info.name );
1593 }
1594
1595 if( FOOTPRINT_VARIANT* fpVariant = footprint->AddVariant( info.name ) )
1596 fpVariant->SetFieldValue( fieldName, fieldValue );
1597 }
1598
1599 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1600 changed = true;
1601 }
1602 }
1603 }
1604
1605 for( const wxString& excess : excessVariants )
1606 {
1607 if( m_isDryRun )
1608 {
1609 msg.Printf( _( "Remove variant %s:%s no longer associated with footprint %s." ),
1610 footprint->GetReference(),
1611 excess,
1612 footprint->GetFPIDAsString() );
1613 }
1614 else
1615 {
1616 msg.Printf( _( "Removed variant %s:%s no longer associated with footprint %s." ),
1617 footprint->GetReference(),
1618 excess,
1619 footprint->GetFPIDAsString() );
1620
1621 footprint->DeleteVariant( excess );
1622 }
1623
1624 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1625 changed = true;
1626 }
1627
1628 // For the default variant: if this footprint is not the base footprint
1629 // it should be DNP by default
1630 bool isBaseFootprint = ( footprint->GetFPID() == aBaseFpid );
1631
1632 if( !isBaseFootprint && !footprint->IsDNP() )
1633 {
1634 msg.Printf( m_isDryRun ? _( "Add %s 'Do not place' fabrication attribute." )
1635 : _( "Added %s 'Do not place' fabrication attribute." ),
1636 footprint->GetReference() );
1637
1638 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1639
1640 if( !m_isDryRun )
1641 footprint->SetDNP( true );
1642
1643 changed = true;
1644 }
1645
1646 if( !m_isDryRun && changed && copy )
1647 m_commit.Modified( footprint, copy );
1648 else
1649 delete copy;
1650 }
1651}
1652
1653
1655{
1656 for( ZONE* zone : m_board->Zones() )
1657 {
1658 if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
1659 continue;
1660
1661 m_zoneConnectionsCache[ zone ] = m_board->GetConnectivity()->GetConnectedPads( zone );
1662 }
1663}
1664
1665
1667{
1668 wxString msg;
1669 std::set<wxString> netlistNetnames;
1670
1671 for( int ii = 0; ii < (int) aNetlist.GetCount(); ii++ )
1672 {
1673 const COMPONENT* component = aNetlist.GetComponent( ii );
1674
1675 for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
1676 {
1677 const COMPONENT_NET& net = component->GetNet( jj );
1678 netlistNetnames.insert( net.GetNetName() );
1679 }
1680 }
1681
1682 for( PCB_TRACK* via : m_board->Tracks() )
1683 {
1684 if( via->Type() != PCB_VIA_T )
1685 continue;
1686
1687 if( netlistNetnames.count( via->GetNetname() ) == 0 )
1688 {
1689 wxString updatedNetname = wxEmptyString;
1690
1691 // Take via name from name change map if it didn't match to a new pad
1692 // (this is useful for stitching vias that don't connect to tracks)
1693 if( m_oldToNewNets.count( via->GetNetname() ) )
1694 {
1695 updatedNetname = m_oldToNewNets[via->GetNetname()];
1696 }
1697
1698 if( !updatedNetname.IsEmpty() )
1699 {
1700 if( m_isDryRun )
1701 {
1702 wxString originalNetname = via->GetNetname();
1703
1704 msg.Printf( _( "Reconnect via from %s to %s." ),
1705 EscapeHTML( UnescapeString( originalNetname ) ),
1706 EscapeHTML( UnescapeString( updatedNetname ) ) );
1707
1708 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1709 }
1710 else
1711 {
1712 NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
1713
1714 if( !netinfo )
1715 netinfo = m_addedNets[updatedNetname];
1716
1717 if( netinfo )
1718 {
1719 wxString originalNetname = via->GetNetname();
1720
1721 m_commit.Modify( via );
1722 via->SetNet( netinfo );
1723
1724 msg.Printf( _( "Reconnected via from %s to %s." ),
1725 EscapeHTML( UnescapeString( originalNetname ) ),
1726 EscapeHTML( UnescapeString( updatedNetname ) ) );
1727
1728 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1729 }
1730 }
1731 }
1732 else
1733 {
1734 msg.Printf( _( "Via connected to unknown net (%s)." ),
1735 EscapeHTML( UnescapeString( via->GetNetname() ) ) );
1736 m_reporter->Report( msg, RPT_SEVERITY_WARNING );
1738 }
1739 }
1740 }
1741
1742 // Board connectivity net names are not the same as schematic connectivity net names.
1743 // Footprints that contain multiple overlapping pads with the same number are suffixed
1744 // with "_N" for internal use. Somewhere along the line, these pseudo net names were
1745 // exposed in the zone net name list.
1746 auto isInNetlist = [&]( const wxString& aNetName ) -> bool
1747 {
1748 if( netlistNetnames.count( aNetName ) )
1749 return true;
1750
1751 // If the zone net name is a pseudo net name, check if the root net name is in the net
1752 // list. If so, then this is a valid net.
1753 for( const wxString& netName : netlistNetnames )
1754 {
1755 if( aNetName.StartsWith( netName ) )
1756 return true;
1757 }
1758
1759 return false;
1760 };
1761
1762 // Test copper zones to detect "dead" nets (nets without any pad):
1763 for( ZONE* zone : m_board->Zones() )
1764 {
1765 if( !zone->IsOnCopperLayer() || zone->GetIsRuleArea() )
1766 continue;
1767
1768 if( !isInNetlist( zone->GetNetname() ) )
1769 {
1770 // Look for a pad in the zone's connected-pad-cache which has been updated to
1771 // a new net and use that. While this won't always be the right net, the dead
1772 // net is guaranteed to be wrong.
1773 wxString updatedNetname = wxEmptyString;
1774
1775 for( PAD* pad : m_zoneConnectionsCache[ zone ] )
1776 {
1777 if( getNetname( pad ) != zone->GetNetname() )
1778 {
1779 updatedNetname = getNetname( pad );
1780 break;
1781 }
1782 }
1783
1784 // Take zone name from name change map if it didn't match to a new pad
1785 // (this is useful for zones on internal layers)
1786 if( updatedNetname.IsEmpty() && m_oldToNewNets.count( zone->GetNetname() ) )
1787 {
1788 updatedNetname = m_oldToNewNets[ zone->GetNetname() ];
1789 }
1790
1791 if( !updatedNetname.IsEmpty() )
1792 {
1793 if( m_isDryRun )
1794 {
1795 wxString originalNetname = zone->GetNetname();
1796
1797 if( !zone->GetZoneName().IsEmpty() )
1798 {
1799 msg.Printf( _( "Reconnect copper zone '%s' from %s to %s." ),
1800 zone->GetZoneName(),
1801 EscapeHTML( UnescapeString( originalNetname ) ),
1802 EscapeHTML( UnescapeString( updatedNetname ) ) );
1803 }
1804 else
1805 {
1806 msg.Printf( _( "Reconnect copper zone from %s to %s." ),
1807 EscapeHTML( UnescapeString( originalNetname ) ),
1808 EscapeHTML( UnescapeString( updatedNetname ) ) );
1809 }
1810
1811 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1812 }
1813 else
1814 {
1815 NETINFO_ITEM* netinfo = m_board->FindNet( updatedNetname );
1816
1817 if( !netinfo )
1818 netinfo = m_addedNets[ updatedNetname ];
1819
1820 if( netinfo )
1821 {
1822 wxString originalNetname = zone->GetNetname();
1823
1824 m_commit.Modify( zone );
1825 zone->SetNet( netinfo );
1826
1827 if( !zone->GetZoneName().IsEmpty() )
1828 {
1829 msg.Printf( _( "Reconnected copper zone '%s' from %s to %s." ),
1830 EscapeHTML( zone->GetZoneName() ),
1831 EscapeHTML( UnescapeString( originalNetname ) ),
1832 EscapeHTML( UnescapeString( updatedNetname ) ) );
1833 }
1834 else
1835 {
1836 msg.Printf( _( "Reconnected copper zone from %s to %s." ),
1837 EscapeHTML( UnescapeString( originalNetname ) ),
1838 EscapeHTML( UnescapeString( updatedNetname ) ) );
1839 }
1840
1841 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1842 }
1843 }
1844 }
1845 else
1846 {
1847 if( !zone->GetZoneName().IsEmpty() )
1848 {
1849 msg.Printf( _( "Copper zone '%s' has no pads connected." ),
1850 EscapeHTML( zone->GetZoneName() ) );
1851 }
1852 else
1853 {
1854 wxString layerNames = zone->LayerMaskDescribe();
1855 VECTOR2I pt = zone->GetPosition();
1856
1857 if( m_frame && m_frame->GetPcbNewSettings() )
1858 {
1859 if( m_frame->GetPcbNewSettings()->m_Display.m_DisplayInvertXAxis )
1860 pt.x *= -1;
1861
1862 if( m_frame->GetPcbNewSettings()->m_Display.m_DisplayInvertYAxis )
1863 pt.y *= -1;
1864 }
1865
1866 msg.Printf( _( "Copper zone on %s at (%s, %s) has no pads connected to net \"%s\"." ),
1867 EscapeHTML( layerNames ),
1868 m_frame ? m_frame->MessageTextFromValue( pt.x )
1870 m_frame ? m_frame->MessageTextFromValue( pt.y )
1872 zone->GetNetname() );
1873 }
1874
1875 m_reporter->Report( msg, RPT_SEVERITY_WARNING );
1877 }
1878 }
1879 }
1880
1881 return true;
1882}
1883
1884
1886{
1887 if( !m_transferGroups )
1888 return false;
1889
1890 wxString msg;
1891
1892 for( PCB_GROUP* pcbGroup : m_board->Groups() )
1893 {
1894 NETLIST_GROUP* netlistGroup = aNetlist.GetGroupByUuid( pcbGroup->m_Uuid );
1895
1896 if( netlistGroup == nullptr )
1897 continue;
1898
1899 if( netlistGroup->name != pcbGroup->GetName() )
1900 {
1901 if( m_isDryRun )
1902 {
1903 msg.Printf( _( "Change group name from '%s' to '%s'." ),
1904 EscapeHTML( pcbGroup->GetName() ),
1905 EscapeHTML( netlistGroup->name ) );
1906 }
1907 else
1908 {
1909 msg.Printf( _( "Changed group name from '%s' to '%s'." ),
1910 EscapeHTML( pcbGroup->GetName() ),
1911 EscapeHTML( netlistGroup->name ) );
1912 m_commit.Modify( pcbGroup->AsEdaItem(), nullptr, RECURSE_MODE::NO_RECURSE );
1913 pcbGroup->SetName( netlistGroup->name );
1914 }
1915
1916 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1917 }
1918
1919 if( netlistGroup->libId != pcbGroup->GetDesignBlockLibId() )
1920 {
1921 if( m_isDryRun )
1922 {
1923 msg.Printf( _( "Change group library link from '%s' to '%s'." ),
1924 EscapeHTML( pcbGroup->GetDesignBlockLibId().GetUniStringLibId() ),
1925 EscapeHTML( netlistGroup->libId.GetUniStringLibId() ) );
1926 }
1927 else
1928 {
1929 msg.Printf( _( "Changed group library link from '%s' to '%s'." ),
1930 EscapeHTML( pcbGroup->GetDesignBlockLibId().GetUniStringLibId() ),
1931 EscapeHTML( netlistGroup->libId.GetUniStringLibId() ) );
1932 m_commit.Modify( pcbGroup->AsEdaItem(), nullptr, RECURSE_MODE::NO_RECURSE );
1933 pcbGroup->SetDesignBlockLibId( netlistGroup->libId );
1934 }
1935
1936 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
1937 }
1938 }
1939
1940 return true;
1941}
1942
1943
1945 std::map<COMPONENT*, FOOTPRINT*>& aFootprintMap )
1946{
1947 // Verify that board contains all pads in netlist: if it doesn't then footprints are
1948 // wrong or missing.
1949
1950 wxString msg;
1951 wxString padNumber;
1952
1953 for( int i = 0; i < (int) aNetlist.GetCount(); i++ )
1954 {
1955 COMPONENT* component = aNetlist.GetComponent( i );
1956 FOOTPRINT* footprint = aFootprintMap[component];
1957
1958 if( !footprint ) // It can be missing in partial designs
1959 continue;
1960
1961 // Explore all pins/pads in component
1962 for( unsigned jj = 0; jj < component->GetNetCount(); jj++ )
1963 {
1964 padNumber = component->GetNet( jj ).GetPinName();
1965
1966 if( padNumber.IsEmpty() )
1967 {
1968 // bad symbol, report error
1969 msg.Printf( _( "Symbol %s has pins with no number. These pins can not be matched "
1970 "to pads in %s." ),
1971 component->GetReference(),
1972 EscapeHTML( footprint->GetFPID().Format().wx_str() ) );
1973 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1974 ++m_errorCount;
1975 }
1976 else if( !footprint->FindPadByNumber( padNumber ) )
1977 {
1978 // not found: bad footprint, report error
1979 msg.Printf( _( "%s pad %s not found in %s." ),
1980 component->GetReference(),
1981 EscapeHTML( padNumber ),
1982 EscapeHTML( footprint->GetFPID().Format().wx_str() ) );
1983 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
1984 ++m_errorCount;
1985 }
1986 }
1987 }
1988
1989 return true;
1990}
1991
1992
1994{
1995 FOOTPRINT* lastPreexistingFootprint = nullptr;
1996 COMPONENT* component = nullptr;
1997 wxString msg;
1998 std::unordered_set<wxString> sheetPaths;
1999 std::unordered_set<FOOTPRINT*> usedFootprints;
2000
2001 m_errorCount = 0;
2002 m_warningCount = 0;
2004
2005 std::map<COMPONENT*, FOOTPRINT*> footprintMap;
2006
2007 if( !m_board->Footprints().empty() )
2008 lastPreexistingFootprint = m_board->Footprints().back();
2009
2011
2012 // First mark all nets (except <no net>) as stale; we'll update those which are current
2013 // in the following two loops. Also prepare the component class manager for updates.
2014 //
2015 if( !m_isDryRun )
2016 {
2017 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
2018 net->SetIsCurrent( net->GetNetCode() == 0 );
2019
2020 m_board->GetComponentClassManager().InitNetlistUpdate();
2021 }
2022
2023 // Collect all schematic net names so NC pad deduplication can avoid collisions
2024 for( unsigned ii = 0; ii < aNetlist.GetCount(); ii++ )
2025 {
2026 COMPONENT* comp = aNetlist.GetComponent( ii );
2027
2028 for( unsigned jj = 0; jj < comp->GetNetCount(); jj++ )
2029 m_schematicNetNames.insert( comp->GetNet( jj ).GetNetName() );
2030 }
2031
2032 // Next go through the netlist updating all board footprints which have matching component
2033 // entries and adding new footprints for those that don't.
2034 //
2035 for( unsigned i = 0; i < aNetlist.GetCount(); i++ )
2036 {
2037 component = aNetlist.GetComponent( i );
2038
2039 if( component->GetProperties().count( wxT( "exclude_from_board" ) ) )
2040 continue;
2041
2042 msg.Printf( _( "Processing symbol '%s:%s'." ),
2043 component->GetReference(),
2044 EscapeHTML( component->GetFPID().Format().wx_str() ) );
2045 m_reporter->Report( msg, RPT_SEVERITY_INFO );
2046
2047 const LIB_ID& baseFpid = component->GetFPID();
2048 const bool hasBaseFpid = !baseFpid.empty();
2049
2050 if( baseFpid.IsLegacy() )
2051 {
2052 msg.Printf( _( "Warning: %s footprint '%s' is missing a library name. "
2053 "Use the full 'Library:Footprint' format to avoid repeated update "
2054 "notifications." ),
2055 component->GetReference(),
2056 EscapeHTML( baseFpid.Format().wx_str() ) );
2057 m_reporter->Report( msg, RPT_SEVERITY_WARNING );
2059 }
2060
2061 std::vector<FOOTPRINT*> matchingFootprints;
2062
2063 for( FOOTPRINT* footprint : m_board->Footprints() )
2064 {
2065 bool match = false;
2066
2068 {
2069 for( const KIID& uuid : component->GetKIIDs() )
2070 {
2071 KIID_PATH base = component->GetPath();
2072 base.push_back( uuid );
2073
2074 if( footprint->GetPath() == base )
2075 {
2076 match = true;
2077 break;
2078 }
2079 }
2080 }
2081 else
2082 {
2083 match = footprint->GetReference().CmpNoCase( component->GetReference() ) == 0;
2084 }
2085
2086 if( match )
2087 matchingFootprints.push_back( footprint );
2088
2089 if( footprint == lastPreexistingFootprint )
2090 {
2091 // No sense going through the newly-created footprints: end of loop
2092 break;
2093 }
2094 }
2095
2096 std::vector<LIB_ID> expectedFpids;
2097 std::unordered_set<wxString> expectedFpidKeys;
2098
2099 auto addExpectedFpid =
2100 [&]( const LIB_ID& aFpid )
2101 {
2102 if( aFpid.empty() )
2103 return;
2104
2105 wxString key = aFpid.Format();
2106
2107 if( expectedFpidKeys.insert( key ).second )
2108 expectedFpids.push_back( aFpid );
2109 };
2110
2111 addExpectedFpid( baseFpid );
2112
2113 const wxString footprintFieldName = GetCanonicalFieldName( FIELD_T::FOOTPRINT );
2114
2115 for( const auto& [variantName, variant] : component->GetVariants() )
2116 {
2117 auto fieldIt = variant.m_fields.find( footprintFieldName );
2118
2119 if( fieldIt == variant.m_fields.end() || fieldIt->second.IsEmpty() )
2120 continue;
2121
2122 LIB_ID parsedId;
2123
2124 if( parsedId.Parse( fieldIt->second, true ) >= 0 )
2125 {
2126 msg.Printf( _( "Invalid footprint ID '%s' for variant '%s' on %s." ),
2127 EscapeHTML( fieldIt->second ),
2128 variantName,
2129 component->GetReference() );
2130 m_reporter->Report( msg, RPT_SEVERITY_ERROR );
2131 ++m_errorCount;
2132 continue;
2133 }
2134
2135 addExpectedFpid( parsedId );
2136 }
2137
2138 // When the schematic-side FPID has no library nickname (legacy format like
2139 // "DGG56" instead of "Package_SO:DGG56"), matching should compare only the
2140 // footprint item name. Otherwise the board footprint (which always has a library
2141 // nickname) will never match, causing perpetual "change footprint" notifications.
2142 auto fpidMatches =
2143 [&]( const LIB_ID& aBoardFpid, const LIB_ID& aExpectedFpid ) -> bool
2144 {
2145 if( aExpectedFpid.IsLegacy() )
2146 return aBoardFpid.GetLibItemName() == aExpectedFpid.GetLibItemName();
2147
2148 return aBoardFpid == aExpectedFpid;
2149 };
2150
2151 auto isExpectedFpid =
2152 [&]( const LIB_ID& aFpid ) -> bool
2153 {
2154 if( aFpid.empty() )
2155 return false;
2156
2157 if( expectedFpidKeys.count( aFpid.Format() ) > 0 )
2158 return true;
2159
2160 for( const LIB_ID& expected : expectedFpids )
2161 {
2162 if( fpidMatches( aFpid, expected ) )
2163 return true;
2164 }
2165
2166 return false;
2167 };
2168
2169 auto takeMatchingFootprint =
2170 [&]( const LIB_ID& aFpid ) -> FOOTPRINT*
2171 {
2172 for( FOOTPRINT* footprint : matchingFootprints )
2173 {
2174 if( usedFootprints.count( footprint ) )
2175 continue;
2176
2177 if( fpidMatches( footprint->GetFPID(), aFpid ) )
2178 return footprint;
2179 }
2180
2181 return nullptr;
2182 };
2183
2184 std::vector<FOOTPRINT*> componentFootprints;
2185 componentFootprints.reserve( expectedFpids.size() );
2186 FOOTPRINT* baseFootprint = nullptr;
2187
2188 if( hasBaseFpid )
2189 baseFootprint = takeMatchingFootprint( baseFpid );
2190 else if( !matchingFootprints.empty() )
2191 baseFootprint = matchingFootprints.front();
2192
2193 if( !baseFootprint && m_replaceFootprints && !matchingFootprints.empty() )
2194 {
2195 FOOTPRINT* replaceCandidate = nullptr;
2196
2197 for( FOOTPRINT* footprint : matchingFootprints )
2198 {
2199 if( usedFootprints.count( footprint ) )
2200 continue;
2201
2202 if( isExpectedFpid( footprint->GetFPID() ) )
2203 continue;
2204
2205 replaceCandidate = footprint;
2206 break;
2207 }
2208
2209 if( replaceCandidate )
2210 {
2211 FOOTPRINT* replaced = replaceFootprint( aNetlist, replaceCandidate, component );
2212
2213 if( replaced )
2214 baseFootprint = replaced;
2215 else
2216 baseFootprint = replaceCandidate;
2217 }
2218 }
2219
2220 if( !baseFootprint && ( hasBaseFpid || expectedFpids.empty() ) )
2221 baseFootprint = addNewFootprint( component, baseFpid );
2222
2223 if( baseFootprint )
2224 {
2225 componentFootprints.push_back( baseFootprint );
2226 usedFootprints.insert( baseFootprint );
2227 footprintMap[ component ] = baseFootprint;
2228 }
2229
2230 for( const LIB_ID& fpid : expectedFpids )
2231 {
2232 if( fpid == baseFpid )
2233 continue;
2234
2235 FOOTPRINT* footprint = takeMatchingFootprint( fpid );
2236
2237 if( !footprint )
2238 footprint = addNewFootprint( component, fpid );
2239
2240 if( footprint )
2241 {
2242 componentFootprints.push_back( footprint );
2243 usedFootprints.insert( footprint );
2244 }
2245 }
2246
2247 for( FOOTPRINT* footprint : componentFootprints )
2248 {
2249 if( !footprint )
2250 continue;
2251
2252 updateFootprintParameters( footprint, component );
2253 updateFootprintGroup( footprint, component );
2254 updateComponentPadConnections( footprint, component );
2255 updateComponentClass( footprint, component );
2256 updateComponentUnits( footprint, component );
2257
2258 sheetPaths.insert( footprint->GetSheetname() );
2259 }
2260
2261 if( !componentFootprints.empty() )
2262 applyComponentVariants( component, componentFootprints, baseFpid );
2263 }
2264
2265 updateCopperZoneNets( aNetlist );
2266 updateGroups( aNetlist );
2267
2268 // Finally go through the board footprints and update all those that *don't* have matching
2269 // component entries.
2270 //
2271 for( FOOTPRINT* footprint : m_board->Footprints() )
2272 {
2273 bool matched = false;
2274 bool doDelete = m_deleteUnusedFootprints;
2275
2276 if( ( footprint->GetAttributes() & FP_BOARD_ONLY ) > 0 )
2277 doDelete = false;
2278
2279 bool isStaleVariantFootprint = false;
2280
2281 if( usedFootprints.count( footprint ) )
2282 {
2283 matched = true;
2284 }
2285 else
2286 {
2288 component = aNetlist.GetComponentByPath( footprint->GetPath() );
2289 else
2290 component = aNetlist.GetComponentByReference( footprint->GetReference() );
2291
2292 if( component && component->GetProperties().count( wxT( "exclude_from_board" ) ) == 0 )
2293 {
2294 // When replace footprints is enabled and a component has variant footprints,
2295 // footprints matching by reference but not in usedFootprints are stale variant
2296 // footprints that should be replaced/removed.
2297 if( m_replaceFootprints && !component->GetVariants().empty() )
2298 {
2299 matched = false;
2300 isStaleVariantFootprint = true;
2301 }
2302 else
2303 {
2304 matched = true;
2305 }
2306 }
2307 }
2308
2309 // Stale variant footprints should be deleted when m_replaceFootprints is enabled,
2310 // regardless of m_deleteUnusedFootprints setting.
2311 if( isStaleVariantFootprint )
2312 doDelete = true;
2313
2314 if( doDelete && !matched && footprint->IsLocked() && !m_overrideLocks )
2315 {
2316 if( m_isDryRun )
2317 {
2318 msg.Printf( _( "Cannot remove unused footprint %s (footprint is locked)." ),
2319 footprint->GetReference() );
2320 }
2321 else
2322 {
2323 msg.Printf( _( "Could not remove unused footprint %s (footprint is locked)." ),
2324 footprint->GetReference() );
2325 }
2326
2327 m_reporter->Report( msg, RPT_SEVERITY_WARNING );
2329 doDelete = false;
2330 }
2331
2332 if( doDelete && !matched )
2333 {
2334 if( m_isDryRun )
2335 {
2336 msg.Printf( _( "Remove unused footprint %s." ),
2337 footprint->GetReference() );
2338 }
2339 else
2340 {
2341 m_commit.Remove( footprint );
2342 msg.Printf( _( "Removed unused footprint %s." ),
2343 footprint->GetReference() );
2344 }
2345
2346 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
2347 }
2348 else if( !m_isDryRun )
2349 {
2350 if( !matched )
2351 footprint->SetPath( KIID_PATH() );
2352
2353 for( PAD* pad : footprint->Pads() )
2354 {
2355 if( pad->GetNet() )
2356 pad->GetNet()->SetIsCurrent( true );
2357 }
2358 }
2359 }
2360
2361 if( !m_isDryRun )
2362 {
2363 // Finalise the component class manager
2364 m_board->GetComponentClassManager().FinishNetlistUpdate();
2365 m_board->SynchronizeComponentClasses( sheetPaths );
2366
2367 m_board->BuildConnectivity();
2368 testConnectivity( aNetlist, footprintMap );
2369
2370 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
2371 {
2372 if( !net->IsCurrent() )
2373 {
2374 msg.Printf( _( "Removed unused net %s." ),
2375 EscapeHTML( net->GetNetname() ) );
2376 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
2377 }
2378 }
2379
2380 m_board->RemoveUnusedNets( &m_commit );
2381
2382 // Update board variant registry from netlist
2383 const std::vector<wxString>& netlistVariants = aNetlist.GetVariantNames();
2384
2385 if( !netlistVariants.empty() || !m_board->GetVariantNames().empty() )
2386 {
2387 m_reporter->Report( _( "Updating design variants..." ), RPT_SEVERITY_INFO );
2388
2389 auto findBoardVariantName =
2390 [&]( const wxString& aVariantName ) -> wxString
2391 {
2392 for( const wxString& name : m_board->GetVariantNames() )
2393 {
2394 if( name.CmpNoCase( aVariantName ) == 0 )
2395 return name;
2396 }
2397
2398 return wxEmptyString;
2399 };
2400
2401 std::vector<wxString> updatedVariantNames;
2402 updatedVariantNames.reserve( netlistVariants.size() );
2403
2404 for( const wxString& variantName : netlistVariants )
2405 {
2406 wxString actualName = findBoardVariantName( variantName );
2407
2408 if( actualName.IsEmpty() )
2409 {
2410 m_board->AddVariant( variantName );
2411 actualName = findBoardVariantName( variantName );
2412
2413 if( !actualName.IsEmpty() )
2414 {
2415 msg.Printf( _( "Added variant '%s'." ), actualName );
2416 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
2417 }
2418 }
2419
2420 if( actualName.IsEmpty() )
2421 continue;
2422
2423 // Update description if changed
2424 wxString newDescription = aNetlist.GetVariantDescription( variantName );
2425 wxString oldDescription = m_board->GetVariantDescription( actualName );
2426
2427 if( newDescription != oldDescription )
2428 {
2429 m_board->SetVariantDescription( actualName, newDescription );
2430 msg.Printf( _( "Updated description for variant '%s'." ), actualName );
2431 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
2432 }
2433
2434 updatedVariantNames.push_back( actualName );
2435 }
2436
2437 std::vector<wxString> variantsToRemove;
2438
2439 for( const wxString& existingName : m_board->GetVariantNames() )
2440 {
2441 bool found = false;
2442
2443 for( const wxString& variantName : netlistVariants )
2444 {
2445 if( existingName.CmpNoCase( variantName ) == 0 )
2446 {
2447 found = true;
2448 break;
2449 }
2450 }
2451
2452 if( !found )
2453 variantsToRemove.push_back( existingName );
2454 }
2455
2456 for( const wxString& variantName : variantsToRemove )
2457 {
2458 m_board->DeleteVariant( variantName );
2459 msg.Printf( _( "Removed variant '%s'." ), variantName );
2460 m_reporter->Report( msg, RPT_SEVERITY_ACTION );
2461 }
2462
2463 if( !updatedVariantNames.empty() )
2464 m_board->SetVariantNames( updatedVariantNames );
2465 }
2466
2467 // When new footprints are added, the automatic zone refill is disabled because:
2468 // * it creates crashes when calculating dynamic ratsnests if auto refill is enabled.
2469 // (the auto refills rebuild the connectivity with incomplete data)
2470 // * it is useless because zones will be refilled after placing new footprints
2471 m_commit.Push( _( "Update Netlist" ), m_newFootprintsCount ? ZONE_FILL_OP : 0 );
2472
2473 m_board->GetConnectivity()->RefreshNetcodeMap( m_board );
2474
2475 // Netlist is authoritative for chain assignment, so the terminal-pin reapplication
2476 // below starts from a clean slate.
2478
2479 // Net chains may specify a display colour override; lift that into the
2480 // board-side lookup so the PCB painter can use it when highlighting.
2481 for( const auto& [chain, colorStr] : aNetlist.GetNetChainColors() )
2482 {
2483 if( !colorStr.IsEmpty() )
2484 {
2485 KIGFX::COLOR4D color;
2486
2487 if( color.SetFromHexString( colorStr ) )
2488 m_board->SetNetChainColor( chain, color );
2489 }
2490 }
2491
2492 // Net chain class assignments stored in the netlist are mirrored into
2493 // the project-level NET_SETTINGS map so the inNetChainClass() rule
2494 // function can resolve them at DRC time. Both the chain->class map and the
2495 // chain-derived pattern assignments are rebuilt from scratch on each netlist
2496 // update so that removed or renamed chains do not leave stale entries.
2497 std::shared_ptr<NET_SETTINGS>& netSettings = m_board->GetDesignSettings().m_NetSettings;
2498
2499 if( netSettings )
2500 {
2501 netSettings->ClearNetChainClasses();
2502 netSettings->ClearChainPatternAssignments();
2503
2504 for( const auto& [chain, className] : aNetlist.GetSignalChainClasses() )
2505 netSettings->SetNetChainClass( chain, className );
2506
2507 // Net chains may specify a netclass that applies to every member net.
2508 // Push that assignment into the board's netclass map before resyncing.
2509 const std::map<wxString, wxString>& chainClasses = aNetlist.GetNetChainNetClasses();
2510
2511 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
2512 {
2513 const wxString& chainName = net->GetNetChain();
2514
2515 if( chainName.IsEmpty() )
2516 continue;
2517
2518 auto it = chainClasses.find( chainName );
2519
2520 if( it == chainClasses.end() || it->second.IsEmpty() )
2521 continue;
2522
2523 if( netSettings->HasNetclass( it->second ) )
2524 netSettings->SetChainPatternAssignment( net->GetNetname(), it->second );
2525 }
2526
2527 // Always resync after chain cleanup so existing NETINFO_ITEM effective-netclass
2528 // pointers pick up cleared/changed chain entries even when chainClasses is empty.
2529 m_board->SynchronizeNetsAndNetClasses( true );
2530 }
2531 else
2532 {
2533 m_board->SynchronizeNetsAndNetClasses( true );
2534 }
2535
2536 for( const auto& sig : aNetlist.GetNetChainTerminalPins() )
2537 {
2538 PAD* pads[2] = { nullptr, nullptr };
2539
2540 for( size_t i = 0; i < sig.second.size() && i < 2; ++i )
2541 {
2542 const wxString& ref = sig.second[i].first;
2543 const wxString& pin = sig.second[i].second;
2544 FOOTPRINT* fp = m_board->FindFootprintByReference( ref );
2545
2546 if( !fp )
2547 continue;
2548
2549 PAD* candidate = nullptr;
2550 PAD* best = nullptr;
2551 int bestDist = std::numeric_limits<int>::max();
2552 BOX2I bbox = fp->GetBoundingBox();
2553
2554 while( ( candidate = fp->FindPadByNumber( pin, candidate ) ) )
2555 {
2556 VECTOR2I pos = candidate->GetPosition();
2557 int dist = std::min( { pos.x - bbox.GetLeft(), bbox.GetRight() - pos.x,
2558 pos.y - bbox.GetTop(), bbox.GetBottom() - pos.y } );
2559
2560 if( !best || dist < bestDist || ( dist == bestDist && candidate->m_Uuid < best->m_Uuid ) )
2561 {
2562 best = candidate;
2563 bestDist = dist;
2564 }
2565 }
2566
2567 pads[i] = best;
2568 }
2569
2570 for( int i = 0; i < 2; ++i )
2571 {
2572 if( !pads[i] )
2573 continue;
2574
2575 NETINFO_ITEM* termNet = pads[i]->GetNet();
2576
2577 if( !termNet || termNet->GetNetChain() != sig.first )
2578 continue;
2579
2580 for( NETINFO_ITEM* net : m_board->GetNetInfo() )
2581 {
2582 if( net != termNet && net->GetNetChain() == sig.first
2583 && net->GetTerminalPad( i ) )
2584 {
2585 net->ClearTerminalPad( i );
2586 }
2587 }
2588
2589 termNet->SetTerminal( i, pads[i] );
2590 }
2591 }
2592
2593 // Although m_commit will probably also set this, it's not guaranteed, and we need to make
2594 // sure any modification to netclasses gets persisted to project settings through a save.
2595 if( m_frame )
2596 m_frame->OnModify();
2597 }
2598
2599 if( m_isDryRun )
2600 {
2601 for( const std::pair<const wxString, NETINFO_ITEM*>& addedNet : m_addedNets )
2602 delete addedNet.second;
2603
2604 m_addedNets.clear();
2605 }
2606
2607 // Update the ratsnest
2608 m_reporter->ReportTail( wxT( "" ), RPT_SEVERITY_ACTION );
2609 m_reporter->ReportTail( wxT( "" ), RPT_SEVERITY_ACTION );
2610
2611 msg.Printf( _( "Total warnings: %d, errors: %d." ), m_warningCount, m_errorCount );
2612 m_reporter->ReportTail( msg, RPT_SEVERITY_INFO );
2613
2614 return true;
2615}
const char * name
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
#define ZONE_FILL_OP
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
void SetUuid(const KIID &aUuid)
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:316
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 getPinFunction(PAD *aPad)
std::vector< FOOTPRINT * > m_addedFootprints
bool updateFootprintParameters(FOOTPRINT *aFootprint, COMPONENT *aNetlistComponent)
std::map< wxString, wxString > m_oldToNewNets
static void ApplyChainAssignments(BOARD *aBoard, const NETLIST &aNetlist, REPORTER *aReporter, bool aDryRun)
Apply the netlist's chain assignments to every NETINFO_ITEM on the board.
bool updateFootprintGroup(FOOTPRINT *aPcbFootprint, COMPONENT *aNetlistComponent)
std::map< wxString, NETINFO_ITEM * > m_addedNets
std::vector< PCB_GROUP * > m_addedGroups
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 updateComponentClass(FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
bool updateComponentPadConnections(FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
std::map< PAD *, wxString > m_padPinFunctions
void applyComponentVariants(COMPONENT *aComponent, const std::vector< FOOTPRINT * > &aFootprints, const LIB_ID &aBaseFpid)
std::map< ZONE *, std::vector< PAD * > > m_zoneConnectionsCache
FOOTPRINT * replaceFootprint(NETLIST &aNetlist, FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
bool updateComponentUnits(FOOTPRINT *aFootprint, COMPONENT *aNewComponent)
FOOTPRINT * addNewFootprint(COMPONENT *aComponent)
std::set< wxString > m_schematicNetNames
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
const NETINFO_LIST & GetNetInfo() const
Definition board.h:1004
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 GetLeft() const
Definition box2.h:228
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr coord_type GetTop() const
Definition box2.h:229
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.
const wxString & GetNetName() const
const wxString & GetPinFunction() const
const wxString & GetPinName() const
const wxString & GetPinType() const
Store all of the related component information found in a netlist.
const std::vector< UNIT_INFO > & GetUnitInfo() const
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
const CASE_INSENSITIVE_MAP< COMPONENT_VARIANT > & GetVariants() 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()
std::unordered_set< EDA_ITEM * > & GetItems()
Definition eda_group.h:54
wxString GetName() const
Definition eda_group.h:51
void RemoveAll()
Definition eda_group.cpp:90
void RemoveItem(EDA_ITEM *aItem)
Remove item from group.
Definition eda_group.cpp:81
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:62
void SetName(const wxString &aName)
Definition eda_group.h:52
const KIID m_Uuid
Definition eda_item.h:535
virtual EDA_GROUP * GetParentGroup() const
Definition eda_item.h:118
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:385
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:269
Variant information for a footprint.
Definition footprint.h:217
bool HasFieldValue(const wxString &aFieldName) const
Definition footprint.h:264
wxString GetFieldValue(const wxString &aFieldName) const
Get a field value override for this variant.
Definition footprint.h:244
bool GetExcludedFromBOM() const
Definition footprint.h:233
bool GetExcludedFromPosFiles() const
Definition footprint.h:236
bool GetDNP() const
Definition footprint.h:230
bool GetDuplicatePadNumbersAreJumpers() const
Definition footprint.h:1137
void SetPosition(const VECTOR2I &aPos) override
EDA_ANGLE GetOrientation() const
Definition footprint.h:408
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
wxString GetSheetname() const
Definition footprint.h:455
void SetPath(const KIID_PATH &aPath)
Definition footprint.h:453
void SetFilters(const wxString &aFilters)
Definition footprint.h:462
void SetStaticComponentClass(const COMPONENT_CLASS *aClass) const
Sets the component class object pointer for this footprint.
const std::vector< FP_UNIT_INFO > & GetUnitInfo() const
Definition footprint.h:928
void SetAttributes(int aAttributes)
Definition footprint.h:496
void SetSheetfile(const wxString &aSheetfile)
Definition footprint.h:459
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:1144
void SetDuplicatePadNumbersAreJumpers(bool aEnabled)
Definition footprint.h:1138
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:377
int GetAttributes() const
Definition footprint.h:495
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition footprint.h:417
wxString GetFPIDAsString() const
Definition footprint.h:435
wxString GetSheetfile() const
Definition footprint.h:458
const LIB_ID & GetFPID() const
Definition footprint.h:429
void SetReference(const wxString &aReference)
Definition footprint.h:835
bool IsLocked() const override
Definition footprint.h:622
void SetValue(const wxString &aValue)
Definition footprint.h:856
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
void SetUnitInfo(const std::vector< FP_UNIT_INFO > &aUnits)
Definition footprint.h:927
wxString GetFilters() const
Definition footprint.h:461
void SetSheetname(const wxString &aSheetname)
Definition footprint.h:456
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
const wxString & GetValue() const
Definition footprint.h:851
const COMPONENT_CLASS * GetStaticComponentClass() const
Returns the component class for this footprint.
void FixUpPadsForBoard(BOARD *aBoard)
Used post-loading of a footprint to adjust the layers on pads to match board inner layers.
const wxString & GetReference() const
Definition footprint.h:829
const KIID_PATH & GetPath() const
Definition footprint.h:452
VECTOR2I GetPosition() const override
Definition footprint.h:405
PAD * FindPadByNumber(const wxString &aPadNumber, PAD *aSearchAfterMe=nullptr) const
Return a PAD with a matching number.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
bool SetFromHexString(const wxString &aColorString)
Definition color4d.cpp:180
wxString AsString() const
Definition kiid.cpp:365
Definition kiid.h:48
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:52
bool empty() const
Definition lib_id.h:193
wxString GetUniStringLibId() const
Definition lib_id.h:148
UTF8 Format() const
Definition lib_id.cpp:119
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
bool IsLegacy() const
Definition lib_id.h:180
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:116
void SetTerminal(int aIndex, PAD *aPad)
Set the terminal-pad pointer and the persisted UUID at aIndex from a single pad, keeping the two view...
void SetIsCurrent(bool isCurrent)
Definition netinfo.h:154
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:260
Store information read from a netlist along with the flags used to update the NETLIST in the BOARD.
const std::map< wxString, std::vector< std::pair< wxString, wxString > > > & GetNetChainTerminalPins() const
const std::vector< wxString > & GetVariantNames() const
unsigned GetCount() const
COMPONENT * GetComponentByPath(const KIID_PATH &aPath)
Return a COMPONENT by aPath.
COMPONENT * GetComponentByReference(const wxString &aReference)
Return a COMPONENT by aReference.
const std::map< wxString, wxString > & GetNetChainNetClasses() const
const std::map< wxString, wxString > & GetSignalChainClasses() const
NETLIST_GROUP * GetGroupByUuid(const KIID &aUuid)
Return a NETLIST_GROUP by aUuid.
const std::map< wxString, wxString > & GetNetChainColors() const
COMPONENT * GetComponent(unsigned aIndex)
Return the COMPONENT at aIndex.
wxString GetNetChainFor(const wxString &aNet) const
wxString GetVariantDescription(const wxString &aVariantName) const
static REPORTER & GetInstance()
Definition reporter.cpp:124
Definition pad.h:65
const wxString & GetPinFunction() const
Definition pad.h:158
VECTOR2I GetPosition() const override
Definition pad.h:219
The main frame for Pcbnew.
void SetName(const wxString &aName)
Definition pcb_field.h:110
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, bool aCheckSide) override
Definition pcb_text.cpp:355
virtual void SetPosition(const VECTOR2I &aPos) override
Definition pcb_text.h:99
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
Definition pcb_text.cpp:445
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:75
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:104
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:54
@ FP_DNP
Definition footprint.h:91
@ FP_EXCLUDE_FROM_POS_FILES
Definition footprint.h:87
@ FP_BOARD_ONLY
Definition footprint.h:89
@ FP_EXCLUDE_FROM_BOM
Definition footprint.h:88
@ FP_JUST_ADDED
Definition footprint.h:90
@ 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.
CITER next(CITER it)
Definition ptree.cpp:124
@ 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)
bool m_hasExcludedFromPosFiles
nlohmann::ordered_map< wxString, wxString > m_fields
@ 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)
KIBIS_COMPONENT * comp
KIBIS_PIN * pin
VECTOR3I expected(15, 30, 45)
const SHAPE_LINE_CHAIN chain
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687