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