KiCad PCB EDA Suite
Loading...
Searching...
No Matches
backannotate.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) 2019 Alexander Shuklin <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25
26#include <backannotate.h>
27#include <boost/property_tree/ptree.hpp>
28#include <confirm.h>
29#include <dsnlexer.h>
30#include <ptree.h>
31#include <reporter.h>
32#include <sch_edit_frame.h>
33#include <sch_sheet_path.h>
34#include <sch_label.h>
35#include <lib_symbol.h>
36#include <schematic.h>
37#include <sch_commit.h>
38#include <string_utils.h>
39#include <kiface_base.h>
41#include <connection_graph.h>
42#include <limits>
43#include <set>
44#include <tool/tool_manager.h>
46#include <tools/sch_selection.h>
48#include <wx/log.h>
49#include <fmt.h>
50#include <fmt/ranges.h>
51#include <fmt/xchar.h>
52
53
54BACK_ANNOTATE::BACK_ANNOTATE( SCH_EDIT_FRAME* aFrame, REPORTER& aReporter, bool aRelinkFootprints,
55 bool aProcessFootprints, bool aProcessValues,
56 bool aProcessReferences, bool aProcessNetNames,
57 bool aProcessAttributes, bool aProcessOtherFields,
58 bool aPreferUnitSwaps, bool aPreferPinSwaps, bool aDryRun ) :
59 m_reporter( aReporter ),
60 m_matchByReference( aRelinkFootprints ),
61 m_processFootprints( aProcessFootprints ),
62 m_processValues( aProcessValues ),
63 m_processReferences( aProcessReferences ),
64 m_processNetNames( aProcessNetNames ),
65 m_processAttributes( aProcessAttributes ),
66 m_processOtherFields( aProcessOtherFields ),
67 m_preferUnitSwaps( aPreferUnitSwaps ),
68 m_preferPinSwaps( aPreferPinSwaps ),
69 m_dryRun( aDryRun ),
70 m_frame( aFrame ),
72{
73}
74
75
79
80
81bool BACK_ANNOTATE::BackAnnotateSymbols( const std::string& aNetlist )
82{
84
87 {
88 m_reporter.ReportTail( _( "Select at least one property to back annotate." ),
90 return false;
91 }
92
93 getPcbModulesFromString( aNetlist );
94
95 SCH_SHEET_LIST sheets = m_frame->Schematic().Hierarchy();
96 sheets.GetSymbols( m_refs, false );
98
101
103
104 return true;
105}
106
107
108bool BACK_ANNOTATE::FetchNetlistFromPCB( std::string& aNetlist )
109{
110 if( Kiface().IsSingle() )
111 {
112 DisplayErrorMessage( m_frame, _( "Cannot fetch PCB netlist because Schematic Editor is opened "
113 "in stand-alone mode.\n"
114 "You must launch the KiCad project manager and create "
115 "a project." ) );
116 return false;
117 }
118
119 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
120
121 if( !frame )
122 {
123 wxFileName fn( m_frame->Prj().GetProjectFullName() );
124 fn.SetExt( FILEEXT::PcbFileExtension );
125
126 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
127 frame->OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ) );
128 }
129
130 m_frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_PCB_GET_NETLIST, aNetlist );
131 return true;
132}
133
134
136{
137 std::string nullPayload;
138
139 m_frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_PCB_UPDATE_LINKS, nullPayload );
140}
141
142
143void BACK_ANNOTATE::getPcbModulesFromString( const std::string& aPayload )
144{
145 auto getStr = []( const PTREE& pt ) -> wxString
146 {
147 return UTF8( pt.front().first );
148 };
149
150 DSNLEXER lexer( aPayload, From_UTF8( __func__ ) );
151 PTREE doc;
152
153 // NOTE: KiCad's PTREE scanner constructs a property *name* tree, not a property tree.
154 // Every token in the s-expr is stored as a property name; the property's value is then
155 // either the nested s-exprs or an empty PTREE; there are *no* literal property values.
156
157 Scan( &doc, &lexer );
158
159 PTREE& tree = doc.get_child( "pcb_netlist" );
160 wxString msg;
161 m_pcbFootprints.clear();
162
163 for( const std::pair<const std::string, PTREE>& item : tree )
164 {
165 wxString path, value, footprint;
166 bool dnp = false, exBOM = false;
167 std::map<wxString, wxString> pinNetMap, fieldsMap;
168 wxASSERT( item.first == "ref" );
169 wxString ref = getStr( item.second );
170
171 try
172 {
174 path = ref;
175 else
176 path = getStr( item.second.get_child( "timestamp" ) );
177
178 if( path == "" )
179 {
180 msg.Printf( _( "Footprint '%s' has no assigned symbol." ), ref );
181 m_reporter.ReportHead( msg, RPT_SEVERITY_WARNING );
182 continue;
183 }
184
185 footprint = getStr( item.second.get_child( "fpid" ) );
186 value = getStr( item.second.get_child( "value" ) );
187
188 // Get child PTREE of fields
189 boost::optional<const PTREE&> fields = item.second.get_child_optional( "fields" );
190
191 // Parse each field out of the fields string
192 if( fields )
193 {
194 for( const std::pair<const std::string, PTREE>& field : fields.get() )
195 {
196 if( field.first != "field" )
197 continue;
198
199 // Fields are of the format "(field (name "name") "12345")
200 const auto& fieldName = field.second.get_child_optional( "name" );
201 const std::string& fieldValue = field.second.back().first;
202
203 if( !fieldName )
204 continue;
205
206 fieldsMap[getStr( fieldName.get() )] = wxString::FromUTF8( fieldValue );
207 }
208 }
209
210
211 // Get DNP and Exclude from BOM out of the properties if they exist
212 for( const auto& child : item.second )
213 {
214 if( child.first != "property" )
215 continue;
216
217 auto property = child.second;
218 auto name = property.get_child_optional( "name" );
219
220 if( !name )
221 continue;
222
223 if( name.get().front().first == "dnp" )
224 {
225 dnp = true;
226 }
227 else if( name.get().front().first == "exclude_from_bom" )
228 {
229 exBOM = true;
230 }
231 }
232
233 boost::optional<const PTREE&> nets = item.second.get_child_optional( "nets" );
234
235 if( nets )
236 {
237 for( const std::pair<const std::string, PTREE>& pin_net : nets.get() )
238 {
239 wxASSERT( pin_net.first == "pin_net" );
240 wxString pinNumber = UTF8( pin_net.second.front().first );
241 wxString netName = UTF8( pin_net.second.back().first );
242 pinNetMap[ pinNumber ] = netName;
243 }
244 }
245 }
246 catch( ... )
247 {
248 wxLogWarning( "Cannot parse PCB netlist for back-annotation." );
249 }
250
251 // Use lower_bound for not to iterate over map twice
252 auto nearestItem = m_pcbFootprints.lower_bound( path );
253
254 if( nearestItem != m_pcbFootprints.end() && nearestItem->first == path )
255 {
256 // Module with this path already exists - generate error
257 msg.Printf( _( "Footprints '%s' and '%s' linked to same symbol." ),
258 nearestItem->second->m_ref,
259 ref );
260 m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
261 }
262 else
263 {
264 // Add footprint to the map
265 auto data = std::make_shared<PCB_FP_DATA>( ref, footprint, value, dnp, exBOM,
266 pinNetMap, fieldsMap );
267 m_pcbFootprints.insert( nearestItem, std::make_pair( path, data ) );
268 }
269 }
270}
271
272
274{
275 for( std::pair<const wxString, std::shared_ptr<PCB_FP_DATA>>& fpData : m_pcbFootprints )
276 {
277 const wxString& pcbPath = fpData.first;
278 auto& pcbData = fpData.second;
279 int refIndex;
280 bool foundInMultiunit = false;
281
282 for( std::pair<const wxString, SCH_REFERENCE_LIST>& item : m_multiUnitsRefs )
283 {
284 SCH_REFERENCE_LIST& refList = item.second;
285
287 refIndex = refList.FindRef( pcbPath );
288 else
289 refIndex = refList.FindRefByFullPath( pcbPath );
290
291 if( refIndex >= 0 )
292 {
293 // If footprint linked to multi unit symbol, we add all symbol's units to
294 // the change list
295 foundInMultiunit = true;
296
297 for( size_t i = 0; i < refList.GetCount(); ++i )
298 {
299 refList[ i ].GetSymbol()->ClearFlags(SKIP_STRUCT );
300 m_changelist.emplace_back( CHANGELIST_ITEM( refList[i], pcbData ) );
301 }
302
303 break;
304 }
305 }
306
307 if( foundInMultiunit )
308 continue;
309
311 refIndex = m_refs.FindRef( pcbPath );
312 else
313 refIndex = m_refs.FindRefByFullPath( pcbPath );
314
315 if( refIndex >= 0 )
316 {
317 m_refs[ refIndex ].GetSymbol()->ClearFlags( SKIP_STRUCT );
318 m_changelist.emplace_back( CHANGELIST_ITEM( m_refs[refIndex], pcbData ) );
319 }
320 else
321 {
322 // Haven't found linked symbol in multiunits or common refs. Generate error
323 wxString msg = wxString::Format( _( "Cannot find symbol for footprint '%s'." ),
324 pcbData->m_ref );
325 m_reporter.ReportTail( msg, RPT_SEVERITY_ERROR );
326 }
327 }
328}
329
331{
332 m_refs.SortByTimeStamp();
333
334 std::sort( m_changelist.begin(), m_changelist.end(),
335 []( const CHANGELIST_ITEM& a, const CHANGELIST_ITEM& b )
336 {
337 return SCH_REFERENCE_LIST::sortByTimeStamp( a.first, b.first );
338 } );
339
340 size_t i = 0;
341
342 for( const std::pair<SCH_REFERENCE, std::shared_ptr<PCB_FP_DATA>>& item : m_changelist )
343 {
344 // Refs and changelist are both sorted by paths, so we just go over m_refs and
345 // generate errors before we will find m_refs member to which item linked
346 while( i < m_refs.GetCount() && m_refs[i].GetPath() != item.first.GetPath() )
347 {
348 const SCH_REFERENCE& ref = m_refs[i];
349
350 if( ref.GetSymbol()->GetExcludedFromBoard() )
351 {
352 wxString msg = wxString::Format( _( "Footprint '%s' is not present on PCB. "
353 "Corresponding symbols in schematic must be "
354 "manually deleted (if desired)." ),
355 m_refs[i].GetRef() );
356 m_reporter.ReportTail( msg, RPT_SEVERITY_WARNING );
357 }
358
359 ++i;
360 }
361
362 ++i;
363 }
364
365 if( m_matchByReference && !m_frame->ReadyToNetlist( _( "Re-linking footprints requires a fully "
366 "annotated schematic." ) ) )
367 {
368 m_reporter.ReportTail( _( "Footprint re-linking canceled by user." ), RPT_SEVERITY_ERROR );
369 }
370}
371
372
374{
375 SCH_COMMIT commit( m_frame );
376 wxString msg;
377
378 std::set<CHANGELIST_ITEM*> unitSwapItems;
379
380 // First, optionally handle unit swaps across multi-unit symbols where possible
381 // This needs to happen before the rest of the normal changelist processing,
382 // because the swaps will modify the changelist
384 {
385 REPORTER& debugReporter = NULL_REPORTER::GetInstance(); // Change to m_reporter for debugging
386
387 // Group changelist items by shared PCB footprint data pointer
388 std::map<std::shared_ptr<PCB_FP_DATA>, std::vector<CHANGELIST_ITEM*>> changesPerFp;
389
390 msg.Printf( wxT( "DEBUG(unit-swap): grouped changelist into %zu footprint(s) for unit-swap processing:" ),
391 changesPerFp.size() );
392
393 // The changelist will have multiple entries for multi-unit symbols, each unit ref, e.g. U1A, U1B
394 // will point to the same PCB_FP_DATA. Grouping by pointer allows us to handle all units of a symbol
395 // together by working on all changes to the same PCB footprint at once.
396 for( CHANGELIST_ITEM& item : m_changelist )
397 {
398 changesPerFp[item.second].push_back( &item );
399 msg += wxT( " " ) + item.first.GetRef();
400 }
401
402 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
403
404 // Handle all changes per footprint
405 for( auto& fpChangelistPair : changesPerFp )
406 {
407 std::set<SCH_SYMBOL*> swappedSymbols;
408 std::set<CHANGELIST_ITEM*> swappedItems;
409 std::shared_ptr<PCB_FP_DATA> fp = fpChangelistPair.first;
410 auto& changedFpItems = fpChangelistPair.second;
411
412 // Build symbol unit list for this footprint (multi-unit candidates)
413 // Snapshot of one schematic unit instance (ref + sheet path + nets) for matching
414 struct SYM_UNIT
415 {
416 SCH_SYMBOL* sym = nullptr;
417 SCH_SCREEN* screen = nullptr;
418 const SCH_SHEET_PATH* sheetPath = nullptr;
419 wxString ref;
420 int currentUnit = 0;
421 // Track these so we avoid re-applying label/field changes to swapped units
422 CHANGELIST_ITEM* changeItem = nullptr;
423 std::map<wxString, wxString> schNetsByPin; // pinNumber -> net (schematic)
424 std::map<wxString, wxString> pcbNetsByPin; // pinNumber -> net (from PCB pin map)
425 std::vector<wxString> unitPinNumbers; // library-defined pin numbers per unit
426 std::vector<wxString> schNetsInUnitOrder;
427 std::vector<wxString> pcbNetsInUnitOrder;
428 };
429
430 // All symbol units for this footprint
431 std::vector<SYM_UNIT> symbolUnits;
432
433 std::map<LIB_SYMBOL*, std::vector<LIB_SYMBOL::UNIT_PIN_INFO>> unitPinsByLibSymbol;
434
435 auto getUnitPins =
436 [&]( SCH_SYMBOL* symbol, int unitNumber ) -> std::vector<wxString>
437 {
438 if( unitNumber <= 0 )
439 return {};
440
441 if( !symbol )
442 return {};
443
444 LIB_SYMBOL* libSymbol = symbol->GetLibSymbolRef().get();
445
446 if( !libSymbol )
447 return {};
448
449 auto found = unitPinsByLibSymbol.find( libSymbol );
450
451 if( found == unitPinsByLibSymbol.end() )
452 found = unitPinsByLibSymbol.emplace( libSymbol,
453 libSymbol->GetUnitPinInfo() ).first;
454
455 const std::vector<LIB_SYMBOL::UNIT_PIN_INFO>& unitInfos = found->second;
456
457 if( unitNumber > static_cast<int>( unitInfos.size() ) )
458 return {};
459
460 return unitInfos[unitNumber - 1].m_pinNumbers;
461 };
462
463 // Compare nets in the deterministic library order so diode arrays and similar map cleanly
464 auto netsInUnitOrder =
465 []( const std::vector<wxString>& pins,
466 const std::map<wxString, wxString>& netByPin )
467 {
468 std::vector<wxString> nets;
469
470 if( !pins.empty() )
471 {
472 nets.reserve( pins.size() );
473
474 for( const wxString& pinNum : pins )
475 {
476 auto it = netByPin.find( pinNum );
477 nets.push_back( it != netByPin.end() ? it->second : wxString() );
478 }
479 }
480 else
481 {
482 nets.reserve( netByPin.size() );
483
484 for( const std::pair<const wxString, wxString>& kv : netByPin )
485 nets.push_back( kv.second );
486 }
487
488 return nets;
489 };
490
491 for( CHANGELIST_ITEM* changedItem : changedFpItems )
492 {
493 SCH_REFERENCE& ref = changedItem->first;
494 SCH_SYMBOL* symbol = ref.GetSymbol();
495 SCH_SCREEN* screen = ref.GetSheetPath().LastScreen();
496
497 if( !symbol )
498 continue;
499
500 // Collect nets keyed by pin number. Ordering by XY is intentionally avoided
501 // here; see comment in SYM_UNIT above.
502 SYM_UNIT symbolUnit;
503 symbolUnit.sym = symbol;
504 symbolUnit.screen = screen;
505 symbolUnit.ref = symbol->GetRef( &ref.GetSheetPath(), true );
506 symbolUnit.sheetPath = &ref.GetSheetPath();
507 symbolUnit.changeItem = changedItem;
508
509 int currentUnit = ref.GetUnit();
510
511 if( currentUnit <= 0 )
512 currentUnit = symbol->GetUnitSelection( &ref.GetSheetPath() );
513
514 if( currentUnit <= 0 )
515 currentUnit = symbol->GetUnit();
516
517 symbolUnit.currentUnit = currentUnit;
518 symbolUnit.unitPinNumbers = getUnitPins( symbol, symbolUnit.currentUnit );
519
520 const SCH_SHEET_PATH& sheetPath = ref.GetSheetPath();
521
522 for( SCH_PIN* pin : symbol->GetPins( &ref.GetSheetPath() ) )
523 {
524 const wxString& pinNum = pin->GetNumber();
525
526 // PCB nets from footprint pin map
527 auto it = fp->m_pinMap.find( pinNum );
528 symbolUnit.pcbNetsByPin[pinNum] = ( it != fp->m_pinMap.end() ) ? it->second : wxString();
529
530 // Schematic nets from connections
531 if( SCH_PIN* p = symbol->GetPin( pinNum ) )
532 {
533 if( SCH_CONNECTION* connection = p->Connection( &sheetPath ) )
534 symbolUnit.schNetsByPin[pinNum] = connection->Name( true );
535 else
536 symbolUnit.schNetsByPin[pinNum] = wxString();
537 }
538 else
539 symbolUnit.schNetsByPin[pinNum] = wxString();
540 }
541
542 symbolUnit.pcbNetsInUnitOrder = netsInUnitOrder( symbolUnit.unitPinNumbers, symbolUnit.pcbNetsByPin );
543 symbolUnit.schNetsInUnitOrder = netsInUnitOrder( symbolUnit.unitPinNumbers, symbolUnit.schNetsByPin );
544
545 symbolUnits.push_back( symbolUnit );
546 }
547
548 auto vectorToString = []( const std::vector<wxString>& values ) -> wxString
549 {
550 return fmt::format( L"{}", fmt::join( values, L", " ) );
551 };
552
553 auto mapToString = [vectorToString]( const std::map<wxString, wxString>& pinMap ) -> wxString
554 {
555 std::vector<wxString> entries;
556
557 for( const std::pair<const wxString, wxString>& pin : pinMap )
558 entries.push_back( pin.first + '=' + pin.second );
559
560 return vectorToString( entries );
561 };
562
563 msg.Printf( wxT( "DEBUG(unit-swap): footprint %s processed (%zu units, dryRun=%d)." ), fp->m_ref,
564 symbolUnits.size(), m_dryRun ? 1 : 0 );
565 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
566
567 // For debugging, sort the symbol units by ref
568 std::sort( symbolUnits.begin(), symbolUnits.end(),
569 []( const SYM_UNIT& a, const SYM_UNIT& b )
570 {
571 return a.ref < b.ref;
572 } );
573
574 // Store addresses so identical SCH_SYMBOLs on different sheet instances stay unique
575 std::vector<SYM_UNIT*> symbolUnitPtrs;
576
577 for( SYM_UNIT& su : symbolUnits )
578 symbolUnitPtrs.push_back( &su );
579
580 for( const SYM_UNIT& su : symbolUnits )
581 {
582 wxString pcbPins = mapToString( su.pcbNetsByPin );
583 wxString schPins = mapToString( su.schNetsByPin );
584 wxString unitPins = vectorToString( su.unitPinNumbers );
585 wxString pcbUnitNetSeq = vectorToString( su.pcbNetsInUnitOrder );
586 wxString schUnitNetSeq = vectorToString( su.schNetsInUnitOrder );
587
588 msg.Printf( wxT( "DEBUG(unit-swap): unit %d: %s pcbPins[%s] schPins[%s] unitPins[%s] pcbUnitNets[%s] "
589 "schUnitNets[%s]." ),
590 su.currentUnit, su.ref, pcbPins, schPins, unitPins, pcbUnitNetSeq, schUnitNetSeq );
591 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
592 }
593
594 if( symbolUnits.size() < 2 )
595 continue;
596
597 // For each symbol unit, find the target unit whose schematic nets match this unit's
598 // PCB nets when treated as a multiset of net names. This allows units with different
599 // pin numbering (e.g. diode arrays) to be matched while still requiring every net in
600 // the unit to move as a group.
601 // desiredTarget maps each PCB unit to the schematic unit whose nets it matches
602 std::map<SYM_UNIT*, SYM_UNIT*> desiredTarget;
603 std::set<SYM_UNIT*> usedTargets;
604 bool mappingOk = true;
605
606 for( SYM_UNIT* symbolUnit : symbolUnitPtrs )
607 {
608 SYM_UNIT* match = nullptr;
609
610 for( SYM_UNIT* potentialMatch : symbolUnitPtrs )
611 {
612 if( usedTargets.count( potentialMatch ) )
613 continue;
614
615 if( symbolUnit->pcbNetsInUnitOrder == potentialMatch->schNetsInUnitOrder )
616 {
617 match = potentialMatch;
618 break;
619 }
620 }
621
622 if( !match )
623 {
624 wxString pcbPins = mapToString( symbolUnit->pcbNetsByPin );
625 wxString schPins = mapToString( symbolUnit->schNetsByPin );
626 wxString unitPins = vectorToString( symbolUnit->unitPinNumbers );
627 wxString pcbUnitNetSeq = vectorToString( symbolUnit->pcbNetsInUnitOrder );
628 wxString schUnitNetSeq = vectorToString( symbolUnit->schNetsInUnitOrder );
629
630 msg.Printf( wxT( "DEBUG(unit-swap): no schematic match for %s (%s) pcbPins[%s] schPins[%s] "
631 "unitPins[%s] pcbUnitNets[%s] schUnitNets[%s]." ),
632 symbolUnit->ref, fp->m_ref, pcbPins, schPins, unitPins, pcbUnitNetSeq, schUnitNetSeq );
633 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
634 mappingOk = false;
635 break;
636 }
637
638 msg.Printf( wxT( "DEBUG(unit-swap): %s matches %s for footprint %s." ), symbolUnit->ref, match->ref,
639 fp->m_ref );
640 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
641 desiredTarget[symbolUnit] = match;
642 usedTargets.insert( match );
643 }
644
645 if( !mappingOk )
646 {
647 msg.Printf( wxT( "DEBUG(unit-swap): mapping failed for footprint %s." ), fp->m_ref );
648 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
649 continue;
650 }
651
652 // Check if mapping is identity (no swap needed)
653 bool isIdentity = true;
654
655 for( SYM_UNIT* su : symbolUnitPtrs )
656 {
657 auto it = desiredTarget.find( su );
658
659 if( it == desiredTarget.end() || it->second != su )
660 {
661 isIdentity = false;
662 break;
663 }
664 }
665
666 if( isIdentity )
667 {
668 msg.Printf( wxT( "DEBUG(unit-swap): footprint %s already aligned (identity mapping)." ), fp->m_ref );
669 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
670 continue;
671 }
672
673 // Decompose into cycles over the symbols and perform swaps along cycles
674 std::set<SYM_UNIT*> visited;
675
676 for( SYM_UNIT* symbolUnit : symbolUnitPtrs )
677 {
678 SYM_UNIT* start = symbolUnit;
679
680 if( visited.count( start ) )
681 continue;
682
683 // Build cycle starting at 'start'
684 std::vector<SYM_UNIT*> cycle;
685 SYM_UNIT* cur = start;
686
687 while( !visited.count( cur ) )
688 {
689 visited.insert( cur );
690 cycle.push_back( cur );
691
692 auto nextIt = desiredTarget.find( cur );
693
694 if( nextIt == desiredTarget.end() )
695 break;
696
697 SYM_UNIT* nextSym = nextIt->second;
698
699 if( !nextSym || nextSym == cur )
700 break;
701
702 cur = nextSym;
703 }
704
705 if( cycle.size() < 2 )
706 {
707 msg.Printf( wxT( "DEBUG(unit-swap): cycle length %zu for footprint %s starting at %s." ),
708 cycle.size(), fp->m_ref, start->ref );
709 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
710 continue;
711 }
712
713 // Apply swaps along the cycle from end to start,
714 // modify all symbol units in the cycle
715 if( !m_dryRun )
716 {
717 for( SYM_UNIT* s : cycle )
718 commit.Modify( s->sym, s->screen );
719 }
720
721 for( size_t i = cycle.size() - 1; i > 0; --i )
722 {
723 SYM_UNIT* a = cycle[i - 1];
724 SYM_UNIT* b = cycle[i];
725 int aUnit = a->currentUnit;
726 int bUnit = b->currentUnit;
727
728 // Swap unit numbers between a and b
729 if( !m_dryRun )
730 {
731 a->sym->SetUnit( bUnit );
732 b->sym->SetUnit( aUnit );
733
734 if( const SCH_SHEET_PATH* sheet = a->sheetPath )
735 a->sym->SetUnitSelection( sheet, bUnit );
736
737 if( const SCH_SHEET_PATH* sheet = b->sheetPath )
738 b->sym->SetUnitSelection( sheet, aUnit );
739 }
740
741 a->currentUnit = bUnit;
742 b->currentUnit = aUnit;
743
744 swappedSymbols.insert( a->sym );
745 swappedSymbols.insert( b->sym );
746
747 // Track which changelist entries participated so later net-label updates skip them
748 if( a->changeItem )
749 {
750 swappedItems.insert( a->changeItem );
751 unitSwapItems.insert( a->changeItem );
752 }
753
754 if( b->changeItem )
755 {
756 swappedItems.insert( b->changeItem );
757 unitSwapItems.insert( b->changeItem );
758 }
759
760 wxString baseRef = a->sym->GetRef( a->sheetPath, false );
761 wxString unitAString = a->sym->SubReference( aUnit, false );
762 wxString unitBString = b->sym->SubReference( bUnit, false );
763
764 if( unitAString.IsEmpty() )
765 unitAString.Printf( wxT( "%d" ), aUnit );
766
767 if( unitBString.IsEmpty() )
768 unitBString.Printf( wxT( "%d" ), bUnit );
769
770 msg.Printf( _( "Swap %s unit %s with unit %s." ), baseRef, unitAString, unitBString );
771 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
773 }
774
775 msg.Printf( wxT( "DEBUG(unit-swap): applied %zu swap steps for footprint %s." ), cycle.size() - 1,
776 fp->m_ref );
777 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
778 }
779
780 // Remove label updates for swapped symbols by marking their SKIP_STRUCT flag
781 if( !m_dryRun )
782 {
783 for( CHANGELIST_ITEM* changedItem : changedFpItems )
784 {
785 SCH_SYMBOL* symbol = changedItem->first.GetSymbol();
786
787 if( !symbol )
788 continue;
789
790 if( swappedItems.count( changedItem ) || swappedSymbols.count( symbol ) )
791 symbol->SetFlags( SKIP_STRUCT );
792
793 int updatedUnit = symbol->GetUnitSelection( &changedItem->first.GetSheetPath() );
794 changedItem->first.SetUnit( updatedUnit );
795 }
796 }
797 }
798 }
799
800 // Apply changes from change list
801 for( CHANGELIST_ITEM& item : m_changelist )
802 {
803 SCH_REFERENCE& ref = item.first;
804 PCB_FP_DATA& fpData = *item.second;
805 SCH_SYMBOL* symbol = ref.GetSymbol();
806 SCH_SCREEN* screen = ref.GetSheetPath().LastScreen();
807 wxString oldFootprint = ref.GetFootprint();
808 wxString oldValue = ref.GetValue();
809 bool oldDNP = ref.GetSymbol()->GetDNP();
810 bool oldExBOM = ref.GetSymbol()->GetExcludedFromBOM();
811 // Skip prevents us from re-applying label/field changes to units we just swapped
812 bool skip = ( ref.GetSymbol()->GetFlags() & SKIP_STRUCT ) > 0 || unitSwapItems.count( &item ) > 0;
813
814 auto boolString =
815 []( bool b ) -> wxString
816 {
817 return b ? _( "true" ) : _( "false" );
818 };
819
820 if( !m_dryRun )
821 {
822 commit.Modify( symbol, screen, RECURSE_MODE::NO_RECURSE );
823 }
824
825 if( m_processReferences && ref.GetRef() != fpData.m_ref && !skip
826 && !symbol->GetField( FIELD_T::REFERENCE )->HasTextVars() )
827 {
829 msg.Printf( _( "Change %s reference designator to '%s'." ),
830 ref.GetRef(),
831 fpData.m_ref );
832
833 if( !m_dryRun )
834 symbol->SetRef( &ref.GetSheetPath(), fpData.m_ref );
835
836 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
837 }
838
839 if( m_processFootprints && oldFootprint != fpData.m_footprint && !skip
840 && !symbol->GetField( FIELD_T::FOOTPRINT )->HasTextVars() )
841 {
843 msg.Printf( _( "Change %s footprint assignment from '%s' to '%s'." ),
844 ref.GetRef(),
845 EscapeHTML( oldFootprint ),
846 EscapeHTML( fpData.m_footprint ) );
847
848 if( !m_dryRun )
849 symbol->SetFootprintFieldText( fpData.m_footprint );
850
851 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
852 }
853
854 if( m_processValues && oldValue != fpData.m_value && !skip
855 && !symbol->GetField( FIELD_T::VALUE )->HasTextVars() )
856 {
858 msg.Printf( _( "Change %s value from '%s' to '%s'." ),
859 ref.GetRef(),
860 EscapeHTML( oldValue ),
861 EscapeHTML( fpData.m_value ) );
862
863 if( !m_dryRun )
864 symbol->SetValueFieldText( fpData.m_value );
865
866 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
867 }
868
869 if( m_processAttributes && oldDNP != fpData.m_DNP && !skip )
870 {
872 msg.Printf( _( "Change %s 'Do not populate' from '%s' to '%s'." ),
873 ref.GetRef(),
874 boolString( oldDNP ),
875 boolString( fpData.m_DNP ) );
876
877 if( !m_dryRun )
878 symbol->SetDNP( fpData.m_DNP );
879
880 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
881 }
882
883 if( m_processAttributes && oldExBOM != fpData.m_excludeFromBOM && !skip )
884 {
886 msg.Printf( _( "Change %s 'Exclude from bill of materials' from '%s' to '%s'." ),
887 ref.GetRef(),
888 boolString( oldExBOM ),
889 boolString( fpData.m_excludeFromBOM ) );
890
891 if( !m_dryRun )
892 symbol->SetExcludedFromBOM( fpData.m_excludeFromBOM );
893
894 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
895 }
896
897 std::set<wxString> swappedPins;
898
899 // Try to satisfy footprint pad net swaps by moving symbol pins before falling back to labels.
900 if( m_preferPinSwaps && m_processNetNames && !skip )
901 swappedPins = applyPinSwaps( symbol, ref, fpData, &commit );
902
903 if( m_processNetNames && !skip )
904 {
905 for( const std::pair<const wxString, wxString>& entry : fpData.m_pinMap )
906 {
907 const wxString& pinNumber = entry.first;
908 const wxString& shortNetName = entry.second;
909 SCH_PIN* pin = symbol->GetPin( pinNumber );
910
911 // Skip pins that the user preferred to handle with pin swaps in applyPreferredPinSwaps()
912 if( swappedPins.count( pinNumber ) > 0 )
913 continue;
914
915 if( !pin )
916 {
917 msg.Printf( _( "Cannot find %s pin '%s'." ),
918 ref.GetRef(),
919 EscapeHTML( pinNumber ) );
920 m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
921
922 continue;
923 }
924
925 SCH_CONNECTION* connection = pin->Connection( &ref.GetSheetPath() );
926
927 if( connection && connection->Name( true ) != shortNetName )
928 {
929 processNetNameChange( &commit, ref.GetRef(), pin, connection,
930 connection->Name( true ), shortNetName );
931 }
932 }
933 }
934
936 {
937 // Need to handle three cases: existing field, new field, deleted field
938 for( const std::pair<const wxString, wxString>& field : fpData.m_fieldsMap )
939 {
940 const wxString& fpFieldName = field.first;
941 const wxString& fpFieldValue = field.second;
942 SCH_FIELD* symField = symbol->GetField( fpFieldName );
943
944 // Skip fields that are individually controlled
945 if( fpFieldName == GetCanonicalFieldName( FIELD_T::REFERENCE )
946 || fpFieldName == GetCanonicalFieldName( FIELD_T::VALUE ) )
947 {
948 continue;
949 }
950
951 // 1. Existing fields has changed value
952 // PCB Field value is checked against the shown text because this is the value
953 // with all the variables resolved. The footprints field value gets the symbol's
954 // resolved value when the PCB is updated from the schematic.
955 if( symField
956 && !symField->HasTextVars()
957 && symField->GetShownText( &ref.GetSheetPath(), false ) != fpFieldValue )
958 {
960 msg.Printf( _( "Change %s field '%s' value to '%s'." ),
961 ref.GetRef(),
962 EscapeHTML( symField->GetCanonicalName() ),
963 EscapeHTML( fpFieldValue ) );
964
965 if( !m_dryRun )
966 symField->SetText( fpFieldValue );
967
968 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
969 }
970
971 // 2. New field has been added to footprint and needs to be added to symbol
972 if( symField == nullptr )
973 {
975 msg.Printf( _( "Add %s field '%s' with value '%s'." ),
976 ref.GetRef(),
977 EscapeHTML( fpFieldName ),
978 EscapeHTML( fpFieldValue ) );
979
980 if( !m_dryRun )
981 {
982 SCH_FIELD newField( symbol, FIELD_T::USER, fpFieldName );
983 newField.SetText( fpFieldValue );
984 newField.SetTextPos( symbol->GetPosition() );
985 newField.SetVisible( false ); // Don't clutter up the schematic
986 symbol->AddField( newField );
987 }
988
989 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
990 }
991 }
992
993 // 3. Existing field has been deleted from footprint and needs to be deleted from symbol
994 // Check all symbol fields for existence in the footprint field map
995 for( SCH_FIELD& field : symbol->GetFields() )
996 {
997 // Never delete mandatory fields
998 if( field.IsMandatory() )
999 continue;
1000
1001 if( fpData.m_fieldsMap.find( field.GetCanonicalName() ) == fpData.m_fieldsMap.end() )
1002 {
1003 // Field not found in footprint field map, delete it
1005 msg.Printf( _( "Delete %s field '%s.'" ),
1006 ref.GetRef(),
1007 EscapeHTML( field.GetName() ) );
1008
1009 if( !m_dryRun )
1010 symbol->RemoveField( symbol->GetField( field.GetName() ) );
1011
1012 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1013 }
1014 }
1015 }
1016
1017 if( symbol->GetFlags() & SKIP_STRUCT )
1018 symbol->ClearFlags( SKIP_STRUCT );
1019
1020 unitSwapItems.erase( &item );
1021
1022 // TODO: back-annotate netclass changes?
1023 }
1024
1025 if( !m_dryRun )
1026 {
1027 m_frame->RecalculateConnections( &commit, NO_CLEANUP );
1028 m_frame->UpdateNetHighlightStatus();
1029
1030 commit.Push( _( "Update Schematic from PCB" ) );
1031 }
1032}
1033
1034
1036{
1038
1039 // Initial orientation from the pin
1040 switch( aPin->GetLibPin()->GetOrientation() )
1041 {
1042 default:
1043 case PIN_ORIENTATION::PIN_RIGHT: spin = SPIN_STYLE::LEFT; break;
1044 case PIN_ORIENTATION::PIN_UP: spin = SPIN_STYLE::BOTTOM; break;
1045 case PIN_ORIENTATION::PIN_DOWN: spin = SPIN_STYLE::UP; break;
1046 case PIN_ORIENTATION::PIN_LEFT: spin = SPIN_STYLE::RIGHT; break;
1047 }
1048
1049 // Reorient based on the actual symbol orientation now
1050 struct ORIENT
1051 {
1052 int flag;
1053 int n_rots;
1054 int mirror_x;
1055 int mirror_y;
1056 }
1057 orientations[] =
1058 {
1059 { SYM_ORIENT_0, 0, 0, 0 },
1060 { SYM_ORIENT_90, 1, 0, 0 },
1061 { SYM_ORIENT_180, 2, 0, 0 },
1062 { SYM_ORIENT_270, 3, 0, 0 },
1063 { SYM_MIRROR_X + SYM_ORIENT_0, 0, 1, 0 },
1064 { SYM_MIRROR_X + SYM_ORIENT_90, 1, 1, 0 },
1065 { SYM_MIRROR_Y, 0, 0, 1 },
1066 { SYM_MIRROR_X + SYM_ORIENT_270, 3, 1, 0 },
1067 { SYM_MIRROR_Y + SYM_ORIENT_0, 0, 0, 1 },
1068 { SYM_MIRROR_Y + SYM_ORIENT_90, 1, 0, 1 },
1069 { SYM_MIRROR_Y + SYM_ORIENT_180, 2, 0, 1 },
1070 { SYM_MIRROR_Y + SYM_ORIENT_270, 3, 0, 1 }
1071 };
1072
1073 ORIENT o = orientations[ 0 ];
1074
1075 const SCH_SYMBOL* parentSymbol = static_cast<const SCH_SYMBOL*>( aPin->GetParentSymbol() );
1076
1077 if( !parentSymbol )
1078 return spin;
1079
1080 int symbolOrientation = parentSymbol->GetOrientation();
1081
1082 for( const ORIENT& i : orientations )
1083 {
1084 if( i.flag == symbolOrientation )
1085 {
1086 o = i;
1087 break;
1088 }
1089 }
1090
1091 for( int i = 0; i < o.n_rots; i++ )
1092 spin = spin.RotateCCW();
1093
1094 if( o.mirror_x )
1095 spin = spin.MirrorX();
1096
1097 if( o.mirror_y )
1098 spin = spin.MirrorY();
1099
1100 return spin;
1101}
1102
1103
1104void addConnections( SCH_ITEM* aItem, const SCH_SHEET_PATH& aSheetPath,
1105 std::set<SCH_ITEM*>& connectedItems )
1106{
1107 if( connectedItems.insert( aItem ).second )
1108 {
1109 for( SCH_ITEM* connectedItem : aItem->ConnectedItems( aSheetPath ) )
1110 addConnections( connectedItem, aSheetPath, connectedItems );
1111 }
1112}
1113
1114
1115std::set<wxString> BACK_ANNOTATE::applyPinSwaps( SCH_SYMBOL* aSymbol, const SCH_REFERENCE& aReference,
1116 const PCB_FP_DATA& aFpData, SCH_COMMIT* aCommit )
1117{
1118 // Tracks pin numbers that we end up swapping so that the caller can skip any
1119 // duplicate label-only handling for those pins.
1120 std::set<wxString> swappedPins;
1121
1122 if( !aSymbol )
1123 return swappedPins;
1124
1125 SCH_SCREEN* screen = aReference.GetSheetPath().LastScreen();
1126
1127 if( !screen )
1128 return swappedPins;
1129
1130 wxCHECK( m_frame, swappedPins );
1131
1132 // Used to build the list of schematic pins whose current net assignment does not match the PCB.
1133 struct PIN_CHANGE
1134 {
1135 SCH_PIN* pin;
1136 wxString pinNumber;
1137 wxString currentNet;
1138 wxString targetNet;
1139 };
1140
1141 std::vector<PIN_CHANGE> mismatches;
1142
1143 // Helper map that lets us find all pins currently on a given net.
1144 std::map<wxString, std::vector<size_t>> pinsByCurrentNet;
1145
1146 // Build the mismatch list by inspecting each footprint pin that differs from the schematic.
1147 for( const std::pair<const wxString, wxString>& entry : aFpData.m_pinMap )
1148 {
1149 const wxString& pinNumber = entry.first;
1150 const wxString& desiredNet = entry.second;
1151
1152 SCH_PIN* pin = aSymbol->GetPin( pinNumber );
1153
1154 // Ignore power pins and anything marked as non-connectable. Power pins can map to
1155 // hidden/global references, e.g. implicit power connections on logic symbols.
1156 // KiCad pins are currently always connectable, but the extra guard keeps the
1157 // logic robust if alternate pin types (e.g. explicit mechanical/NC pins)
1158 // ever start reporting false.
1159 if( !pin || pin->IsPower() || !pin->IsConnectable() )
1160 continue;
1161
1162 SCH_CONNECTION* connection = pin->Connection( &aReference.GetSheetPath() );
1163 wxString currentNet = connection ? connection->Name( true ) : wxString();
1164
1165 if( desiredNet.IsEmpty() || currentNet.IsEmpty() )
1166 continue;
1167
1168 if( desiredNet == currentNet )
1169 continue;
1170
1171 size_t idx = mismatches.size();
1172 mismatches.push_back( { pin, pinNumber, currentNet, desiredNet } );
1173 pinsByCurrentNet[currentNet].push_back( idx );
1174 }
1175
1176 if( mismatches.size() < 2 )
1177 return swappedPins;
1178
1179 // Track which mismatch entries we have already consumed, and the underlying pin objects
1180 // we still need to clean up wiring around once the geometry swap is done.
1181 std::vector<bool> handled( mismatches.size(), false );
1182 std::vector<SCH_PIN*> swappedPinObjects;
1183 bool swappedLibPins = false;
1184 wxString msg;
1185
1186 bool allowPinSwaps = false;
1187 wxString currentProjectName = m_frame->Prj().GetProjectName();
1188
1189 if( m_frame->eeconfig() )
1190 allowPinSwaps = m_frame->eeconfig()->m_Input.allow_unconstrained_pin_swaps;
1191
1192 std::set<wxString> sharedSheetPaths;
1193 std::set<wxString> sharedProjectNames;
1194 bool sharedSheetSymbol =
1195 SymbolHasSheetInstances( *aSymbol, currentProjectName, &sharedSheetPaths, &sharedProjectNames );
1196
1197 std::set<wxString> friendlySheetNames;
1198
1199 if( sharedSheetSymbol && !sharedSheetPaths.empty() )
1200 friendlySheetNames = GetSheetNamesFromPaths( sharedSheetPaths, m_frame->Schematic() );
1201
1202 // Check each mismatch and try to find a partner whose desired net matches our current net
1203 // (i.e. the two pins have been swapped on the PCB).
1204 for( size_t i = 0; i < mismatches.size(); ++i )
1205 {
1206 // Skip entries already handled by a previous successful swap.
1207 if( handled[i] )
1208 continue;
1209
1210 PIN_CHANGE& change = mismatches[i];
1211
1212 // Find candidate pins whose current net equals the net we want to move this pin to.
1213 auto range = pinsByCurrentNet.find( change.targetNet );
1214
1215 // Nobody currently on the desired net, so there’s no reciprocal swap to apply.
1216 if( range == pinsByCurrentNet.end() )
1217 continue;
1218
1219 // Track the best partner index in case we discover a reciprocal mismatch below.
1220 size_t partnerIdx = std::numeric_limits<size_t>::max();
1221
1222 // Examine every pin that presently lives on the net we want to move to.
1223 for( size_t candidateIdx : range->second )
1224 {
1225 if( candidateIdx == i || handled[candidateIdx] )
1226 continue;
1227
1228 PIN_CHANGE& candidate = mismatches[candidateIdx];
1229
1230 // Potential swap partner must want to move to our current net, i.e. the PCB swapped the
1231 // two nets between these pins.
1232 if( candidate.targetNet == change.currentNet )
1233 {
1234 partnerIdx = candidateIdx;
1235 break;
1236 }
1237 }
1238
1239 // No viable partner found; either there was no reciprocal net mismatch or it was already
1240 // consumed. Leave this entry for label-based handling.
1241 if( partnerIdx == std::numeric_limits<size_t>::max() )
1242 continue;
1243
1244 PIN_CHANGE& partner = mismatches[partnerIdx];
1245
1246 // Sanity check: both pins must belong to the same schematic symbol before we swap geometry;
1247 // this prevents us from moving pin outlines between different units of a multi-unit symbol.
1248 if( change.pin->GetParentSymbol() != partner.pin->GetParentSymbol() )
1249 continue;
1250
1251 if( !allowPinSwaps || sharedSheetSymbol )
1252 {
1253 if( !sharedProjectNames.empty() )
1254 {
1255 std::vector<wxString> otherProjects;
1256
1257 for( const wxString& name : sharedProjectNames )
1258 {
1259 if( !currentProjectName.IsEmpty() && name.IsSameAs( currentProjectName ) )
1260 continue;
1261
1262 otherProjects.push_back( name );
1263 }
1264
1265 wxString projects = AccumulateDescriptions( otherProjects );
1266
1267 if( projects.IsEmpty() )
1268 {
1269 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but the symbol is shared across other projects." ),
1270 aReference.GetRef(), EscapeHTML( change.pin->GetShownNumber() ),
1271 EscapeHTML( partner.pin->GetShownNumber() ) );
1272 }
1273 else
1274 {
1275 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but the symbol is shared across other "
1276 "projects (%s)." ),
1277 aReference.GetRef(), EscapeHTML( change.pin->GetShownNumber() ),
1278 EscapeHTML( partner.pin->GetShownNumber() ), projects );
1279 }
1280 }
1281 else if( !friendlySheetNames.empty() )
1282 {
1283 wxString sheets = AccumulateDescriptions( friendlySheetNames );
1284
1285 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but the symbol is used by multiple sheet "
1286 "instances (%s)." ),
1287 aReference.GetRef(), EscapeHTML( change.pin->GetShownNumber() ),
1288 EscapeHTML( partner.pin->GetShownNumber() ), sheets );
1289 }
1290 else if( sharedSheetSymbol )
1291 {
1292 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but the symbol is shared." ),
1293 aReference.GetRef(), EscapeHTML( change.pin->GetShownNumber() ),
1294 EscapeHTML( partner.pin->GetShownNumber() ) );
1295 }
1296 else
1297 {
1298 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but unconstrained pin swaps are disabled in "
1299 "the schematic preferences." ),
1300 aReference.GetRef(), EscapeHTML( change.pin->GetShownNumber() ),
1301 EscapeHTML( partner.pin->GetShownNumber() ) );
1302 }
1303 m_reporter.ReportHead( msg, RPT_SEVERITY_INFO );
1304
1305 handled[i] = true;
1306 handled[partnerIdx] = true;
1307 continue;
1308 }
1309
1310 if( !m_dryRun )
1311 {
1312 wxCHECK2( aCommit, continue );
1313
1314 // Record the two pins in the commit and physically swap their local geometry.
1315 aCommit->Modify( change.pin, screen, RECURSE_MODE::RECURSE );
1316 aCommit->Modify( partner.pin, screen, RECURSE_MODE::RECURSE );
1317
1318 swappedLibPins |= SwapPinGeometry( change.pin, partner.pin );
1319 }
1320
1321 // Don’t pick either entry again for another pairing.
1322 handled[i] = true;
1323 handled[partnerIdx] = true;
1324
1325 // Remember which pin numbers we touched so the caller can suppress duplicate work.
1326 swappedPins.insert( change.pinNumber );
1327 swappedPins.insert( partner.pinNumber );
1328 swappedPinObjects.push_back( change.pin );
1329 swappedPinObjects.push_back( partner.pin );
1330
1332
1333 msg.Printf( _( "Swap %s pins %s and %s to match PCB." ), aReference.GetRef(),
1334 EscapeHTML( change.pin->GetShownNumber() ), EscapeHTML( partner.pin->GetShownNumber() ) );
1335 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1336 }
1337
1338 // Nothing left to do if we didn't find a valid swap pair when we were in dry-run mode.
1339 if( swappedPinObjects.empty() )
1340 return swappedPins;
1341
1342 if( !m_dryRun )
1343 {
1344 if( swappedLibPins )
1345 aSymbol->UpdatePins();
1346
1347 m_frame->UpdateItem( aSymbol, false, true );
1348
1349 if( TOOL_MANAGER* toolMgr = m_frame->GetToolManager() )
1350 {
1351 if( SCH_LINE_WIRE_BUS_TOOL* lwbTool = toolMgr->GetTool<SCH_LINE_WIRE_BUS_TOOL>() )
1352 {
1353 SCH_SELECTION cleanupSelection( screen );
1354
1355 // Make sure we tidy up any wires connected to the pins whose geometry just moved.
1356 for( SCH_PIN* swappedPin : swappedPinObjects )
1357 cleanupSelection.Add( swappedPin );
1358
1359 lwbTool->TrimOverLappingWires( aCommit, &cleanupSelection );
1360 lwbTool->AddJunctionsIfNeeded( aCommit, &cleanupSelection );
1361 }
1362 }
1363
1364 m_frame->Schematic().CleanUp( aCommit );
1365 }
1366
1367 return swappedPins;
1368}
1369
1370
1371void BACK_ANNOTATE::processNetNameChange( SCH_COMMIT* aCommit, const wxString& aRef, SCH_PIN* aPin,
1372 const SCH_CONNECTION* aConnection,
1373 const wxString& aOldName, const wxString& aNewName )
1374{
1375 wxString msg;
1376
1377 // Find a physically-connected driver. We can't use the SCH_CONNECTION's m_driver because
1378 // it has already been resolved by merging subgraphs with the same label, etc., and our
1379 // name change may cause that resolution to change.
1380
1381 std::set<SCH_ITEM*> connectedItems;
1382 SCH_ITEM* driver = nullptr;
1384
1385 addConnections( aPin, aConnection->Sheet(), connectedItems );
1386
1387 for( SCH_ITEM* item : connectedItems )
1388 {
1390
1391 if( priority > driverPriority )
1392 {
1393 driver = item;
1394 driverPriority = priority;
1395 }
1396 }
1397
1398 switch( driver->Type() )
1399 {
1400 case SCH_LABEL_T:
1401 case SCH_GLOBAL_LABEL_T:
1402 case SCH_HIER_LABEL_T:
1403 case SCH_SHEET_PIN_T:
1405
1406 msg.Printf( _( "Change %s pin %s net label from '%s' to '%s'." ),
1407 aRef,
1408 EscapeHTML( aPin->GetShownNumber() ),
1409 EscapeHTML( aOldName ),
1410 EscapeHTML( aNewName ) );
1411
1412 if( !m_dryRun )
1413 {
1414 aCommit->Modify( driver, aConnection->Sheet().LastScreen() );
1415 static_cast<SCH_LABEL_BASE*>( driver )->SetText( aNewName );
1416 }
1417
1418 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1419 break;
1420
1421 case SCH_PIN_T:
1422 {
1423 SCH_PIN* schPin = static_cast<SCH_PIN*>( driver );
1424 SPIN_STYLE spin = orientLabel( schPin );
1425
1426 if( schPin->IsPower() )
1427 {
1428 msg.Printf( _( "Net %s cannot be changed to %s because it is driven by a power pin." ),
1429 EscapeHTML( aOldName ),
1430 EscapeHTML( aNewName ) );
1431
1432 m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
1433 break;
1434 }
1435
1437 msg.Printf( _( "Add label '%s' to %s pin %s net." ),
1438 EscapeHTML( aNewName ),
1439 aRef,
1440 EscapeHTML( aPin->GetShownNumber() ) );
1441
1442 if( !m_dryRun )
1443 {
1444 SCHEMATIC_SETTINGS& settings = m_frame->Schematic().Settings();
1445 SCH_LABEL* label = new SCH_LABEL( driver->GetPosition(), aNewName );
1446 label->SetParent( &m_frame->Schematic() );
1447 label->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
1448 label->SetSpinStyle( spin );
1449 label->SetFlags( IS_NEW );
1450
1451 SCH_SCREEN* screen = aConnection->Sheet().LastScreen();
1452 aCommit->Add( label, screen );
1453 }
1454
1455 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1456 }
1457 break;
1458
1459 default:
1460 break;
1461 }
1462}
const char * name
void addConnections(SCH_ITEM *aItem, const SCH_SHEET_PATH &aSheetPath, std::set< SCH_ITEM * > &connectedItems)
static SPIN_STYLE orientLabel(SCH_PIN *aPin)
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
bool BackAnnotateSymbols(const std::string &aNetlist)
Run back annotation algorithm.
SCH_MULTI_UNIT_REFERENCE_MAP m_multiUnitsRefs
std::deque< CHANGELIST_ITEM > m_changelist
std::pair< SCH_REFERENCE, std::shared_ptr< PCB_FP_DATA > > CHANGELIST_ITEM
bool m_processReferences
bool m_processFootprints
bool m_processOtherFields
void getPcbModulesFromString(const std::string &aPayload)
Parse netlist sent over KiWay express mail interface and fill m_pcbModules.
SCH_EDIT_FRAME * m_frame
void checkForUnusedSymbols()
Check if some symbols are not represented in PCB footprints and vice versa.
SCH_REFERENCE_LIST m_refs
bool FetchNetlistFromPCB(std::string &aNetlist)
Get netlist from the Pcbnew.
PCB_FOOTPRINTS_MAP m_pcbFootprints
void processNetNameChange(SCH_COMMIT *aCommit, const wxString &aRef, SCH_PIN *aPin, const SCH_CONNECTION *aConnection, const wxString &aOldName, const wxString &aNewName)
REPORTER & m_reporter
bool m_processAttributes
BACK_ANNOTATE(SCH_EDIT_FRAME *aFrame, REPORTER &aReporter, bool aRelinkFootprints, bool aProcessFootprints, bool aProcessValues, bool aProcessReferences, bool aProcessNetNames, bool aProcessAttributes, bool aProcessOtherFields, bool aPreferUnitSwaps, bool aPreferPinSwaps, bool aDryRun)
std::set< wxString > applyPinSwaps(SCH_SYMBOL *aSymbol, const SCH_REFERENCE &aReference, const PCB_FP_DATA &aFpData, SCH_COMMIT *aCommit)
Handle footprint pad net swaps with symbol pin swaps where possible.
bool m_matchByReference
void PushNewLinksToPCB()
void applyChangelist()
Apply changelist to the schematic.
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
Implement a lexical analyzer for the SPECCTRA DSN file format.
Definition dsnlexer.h:81
virtual VECTOR2I GetPosition() const
Definition eda_item.h:272
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:142
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:144
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:145
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:544
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:589
bool HasTextVars() const
Indicates the ShownText has text var references which need to be processed.
Definition eda_text.h:117
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:397
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
virtual bool OpenProjectFiles(const std::vector< wxString > &aFileList, int aCtl=0)
Open a project or set of files given by aFileList.
Define a library symbol object.
Definition lib_symbol.h:87
std::vector< UNIT_PIN_INFO > GetUnitPinInfo() const
Return pin-number lists for each unit, ordered consistently for gate swapping.
static REPORTER & GetInstance()
Definition reporter.cpp:96
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & ReportHead(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Places the report at the beginning of the list for objects that support ordering.
Definition reporter.h:121
These are loaded from Eeschema settings but then overwritten by the project settings.
virtual void Push(const wxString &aMessage=wxT("A commit"), int aCommitFlags=0) override
Execute the changes.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
SCH_SHEET_PATH Sheet() const
wxString Name(bool aIgnoreSheet=false) const
Schematic editor (Eeschema) main window.
wxString GetCanonicalName() const
Get a non-language-specific name for a field which can be used for storage, variable look-up,...
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
void SetText(const wxString &aText) override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
const SCH_ITEM_VEC & ConnectedItems(const SCH_SHEET_PATH &aPath)
Retrieve the set of items connected to this item on the given sheet.
Definition sch_item.cpp:411
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:250
int GetUnit() const
Definition sch_item.h:238
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Tool responsible for drawing/placing items (symbols, wires, buses, labels, etc.)
wxString GetShownNumber() const
Definition sch_pin.cpp:587
SCH_PIN * GetLibPin() const
Definition sch_pin.h:89
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:263
bool IsPower() const
Check if the pin is either a global or local power pin.
Definition sch_pin.cpp:380
Container to create a flattened list of symbols because in a complex hierarchy, a symbol can be used ...
int FindRef(const wxString &aPath) const
Search the list for a symbol with a given reference.
int FindRefByFullPath(const wxString &aFullPath) const
Search the list for a symbol with the given KIID path (as string).
A helper to define a symbol's reference designator in a schematic.
const SCH_SHEET_PATH & GetSheetPath() const
const wxString GetFootprint() const
SCH_SYMBOL * GetSymbol() const
wxString GetRef() const
const wxString GetValue() const
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void GetMultiUnitSymbols(SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, bool aIncludePowerSymbols=true) const
Add a SCH_REFERENCE_LIST object to aRefList for each same-reference set of multi-unit parts in the li...
void GetSymbols(SCH_REFERENCE_LIST &aReferences, bool aIncludePowerSymbols=true, bool aForceIncludeOrphanSymbols=false) const
Add a SCH_REFERENCE object to aReferences for each symbol in the list of sheets.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SCREEN * LastScreen()
Schematic symbol object.
Definition sch_symbol.h:75
virtual void SetDNP(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
void SetValueFieldText(const wxString &aValue)
void RemoveField(const wxString &aFieldName)
Remove a user field from the symbol.
bool GetExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
void UpdatePins()
Updates the cache of SCH_PIN objects for each pin.
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
void SetFootprintFieldText(const wxString &aFootprint)
VECTOR2I GetPosition() const override
Definition sch_symbol.h:808
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
SCH_PIN * GetPin(const wxString &number) const
Find a symbol pin by number.
int GetOrientation() const override
Get the display symbol orientation.
void SetExcludedFromBOM(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear the exclude from schematic bill of materials flag.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:183
virtual bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flag.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
virtual void Add(EDA_ITEM *aItem)
Definition selection.cpp:42
SPIN_STYLE MirrorX()
Mirror the label spin style across the X axis or simply swaps up and bottom.
SPIN_STYLE MirrorY()
Mirror the label spin style across the Y axis or simply swaps left and right.
SPIN_STYLE RotateCCW()
bool GetExcludedFromBoard() const override
Definition symbol.h:206
Master controller class:
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:72
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:194
This file is part of the common library.
#define _(s)
@ RECURSE
Definition eda_item.h:51
@ NO_RECURSE
Definition eda_item.h:52
#define IS_NEW
New item, just created.
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
@ MAIL_PCB_UPDATE_LINKS
Definition mail_type.h:52
@ MAIL_PCB_GET_NETLIST
Definition mail_type.h:51
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:127
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:111
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:118
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:135
void Scan(PTREE *aTree, DSNLEXER *aLexer)
Fill an empty PTREE with information from a KiCad s-expression stream.
Definition ptree.cpp:84
boost::property_tree::ptree PTREE
Definition ptree.h:52
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
std::set< wxString > GetSheetNamesFromPaths(const std::set< wxString > &aSheetPaths, const SCHEMATIC &aSchematic)
Get human-readable sheet names from a set of sheet paths, e.g.
bool SwapPinGeometry(SCH_PIN *aFirst, SCH_PIN *aSecond)
Swap the positions/lengths/etc.
bool SymbolHasSheetInstances(const SCH_SYMBOL &aSymbol, const wxString &aCurrentProject, std::set< wxString > *aSheetPaths, std::set< wxString > *aProjectNames)
Returns true when the given symbol has instances, e.g.
@ NO_CLEANUP
Definition schematic.h:75
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
wxString From_UTF8(const char *cstring)
void AccumulateDescriptions(wxString &aDesc, const T &aItemCollection)
Build a comma-separated list from a collection of wxStrings.
Container for Pcbnew footprint data.Map to hold NETLIST footprints data.
std::map< wxString, wxString > m_pinMap
std::map< wxString, wxString > m_fieldsMap
@ SYM_ORIENT_270
Definition symbol.h:42
@ SYM_MIRROR_Y
Definition symbol.h:44
@ SYM_ORIENT_180
Definition symbol.h:41
@ SYM_MIRROR_X
Definition symbol.h:43
@ SYM_ORIENT_90
Definition symbol.h:40
@ SYM_ORIENT_0
Definition symbol.h:39
@ 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)
#define kv
@ SCH_LABEL_T
Definition typeinfo.h:171
@ SCH_HIER_LABEL_T
Definition typeinfo.h:173
@ SCH_SHEET_PIN_T
Definition typeinfo.h:178
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:172
@ SCH_PIN_T
Definition typeinfo.h:157
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.