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