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, see <https://www.gnu.org/licenses/>.
19 */
20
21
22#include <backannotate.h>
23#include <boost/property_tree/ptree.hpp>
24#include <confirm.h>
25#include <common.h>
26#include <dsnlexer.h>
27#include <ptree.h>
28#include <reporter.h>
29#include <sch_edit_frame.h>
30#include <sch_sheet_path.h>
31#include <sch_label.h>
32#include <lib_symbol.h>
33#include <schematic.h>
34#include <advanced_config.h>
36#include <sch_commit.h>
37#include <string_utils.h>
38#include <kiface_base.h>
40#include <connection_graph.h>
41#include <limits>
42#include <set>
43#include <tool/tool_manager.h>
45#include <tools/sch_selection.h>
47#include <wx/log.h>
48#include <fmt.h>
49#include <fmt/ranges.h>
50#include <fmt/xchar.h>
51
52// Compare nets in the deterministic library order so diode arrays and similar map cleanly.
53static std::vector<wxString> netsInUnitOrder( const std::vector<wxString>& aPins,
54 const std::map<wxString, wxString>& aNetByPin )
55{
56 std::vector<wxString> nets;
57
58 if( !aPins.empty() )
59 {
60 for( const wxString& pinNum : aPins )
61 {
62 auto it = aNetByPin.find( pinNum );
63 nets.push_back( it != aNetByPin.end() ? it->second : wxString() );
64 }
65 }
66 else
67 {
68 for( const std::pair<const wxString, wxString>& kv : aNetByPin )
69 nets.push_back( kv.second );
70 }
71
72 return nets;
73}
74
75
76// The board reports pad numbers, the unit planner compares logical pin numbers.
77bool CollectBackannotatePcbNets( const SCH_SYMBOL& aSymbol, const SCH_SHEET_PATH& aSheetPath,
78 const wxString& aVariantName, const std::map<wxString, wxString>& aPcbNetsByPad,
79 std::map<wxString, wxString>& aPcbNetsByLogicalPin )
80{
81 for( const SCH_PIN* pin : aSymbol.GetPins( &aSheetPath ) )
82 {
83 // Pins on symbols can have more than one pin stacked
84 bool logicalPinsValid = false;
85 const std::vector<wxString> logicalPins = pin->GetStackedPinNumbers( &logicalPinsValid );
86
87 if( !logicalPinsValid || logicalPins.empty() )
88 return false;
89
90 // Map those multiple stacked pins to whatever we have them mapped to on the PCB side
91 bool padsValid = false;
92 const std::vector<wxString> pads =
93 ExpandStackedPinNotation( pin->GetEffectivePadNumber( aSheetPath, aVariantName ), &padsValid );
94
95 if( !padsValid || pads.empty() )
96 return false;
97
98 // Get all our nets now that we have all our pads
99 std::set<wxString> pcbNets;
100
101 for( const wxString& pad : pads )
102 {
103 auto it = aPcbNetsByPad.find( pad );
104 pcbNets.insert( it != aPcbNetsByPad.end() ? it->second : wxString() );
105 }
106
107 // A stacked pin represents one schematic net. More than one pad net corresponding
108 // to a single stacked pin cannot be resolved by a unit swap
109 if( pcbNets.size() != 1 )
110 return false;
111
112 const wxString& pcbNet = *pcbNets.begin();
113
114 for( const wxString& pinNum : logicalPins )
115 {
116 auto [it, inserted] = aPcbNetsByLogicalPin.emplace( pinNum, pcbNet );
117
118 // We might already have some mappings from previous calls to this function,
119 // same mappings are fine, but new different mappings are not
120 if( !inserted && it->second != pcbNet )
121 return false;
122 }
123 }
124
125 return true;
126}
127
128
130 const std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE>& aCandidates,
131 const std::map<wxString, wxString>& aPcbPinMap )
132{
134
135 if( aCandidates.size() < 2 )
136 {
137 plan.m_mappingOk = true;
138 return plan;
139 }
140
141 std::map<size_t, size_t> desiredTarget;
142 std::set<size_t> usedTargets;
143
144 for( size_t candidateIdx = 0; candidateIdx < aCandidates.size(); ++candidateIdx )
145 {
146 const BACKANNOTATE_UNIT_SWAP_CANDIDATE& candidate = aCandidates[candidateIdx];
147 std::map<wxString, wxString> pcbNetsByPin;
148
149 for( const wxString& pinNum : candidate.m_unitPinNumbers )
150 {
151 auto it = aPcbPinMap.find( pinNum );
152 pcbNetsByPin[pinNum] = ( it != aPcbPinMap.end() ) ? it->second : wxString();
153 }
154
155 std::vector<wxString> pcbNets = netsInUnitOrder( candidate.m_unitPinNumbers, pcbNetsByPin );
156 bool matched = false;
157
158 for( size_t matchIdx = 0; matchIdx < aCandidates.size(); ++matchIdx )
159 {
160 if( usedTargets.count( matchIdx ) )
161 continue;
162
163 const BACKANNOTATE_UNIT_SWAP_CANDIDATE& potentialMatch = aCandidates[matchIdx];
164 std::vector<wxString> schNets =
165 netsInUnitOrder( potentialMatch.m_unitPinNumbers, potentialMatch.m_schNetsByPin );
166
167 if( pcbNets == schNets )
168 {
169 desiredTarget[candidateIdx] = matchIdx;
170 usedTargets.insert( matchIdx );
171 matched = true;
172 break;
173 }
174 }
175
176 if( !matched )
177 return plan;
178 }
179
180 plan.m_mappingOk = true;
181 plan.m_identity = true;
182
183 for( size_t candidateIdx = 0; candidateIdx < aCandidates.size(); ++candidateIdx )
184 {
185 auto it = desiredTarget.find( candidateIdx );
186
187 if( it == desiredTarget.end() || it->second != candidateIdx )
188 {
189 plan.m_identity = false;
190 break;
191 }
192 }
193
194 if( plan.m_identity )
195 return plan;
196
197 std::set<size_t> visited;
198
199 for( size_t startIdx = 0; startIdx < aCandidates.size(); ++startIdx )
200 {
201 if( visited.count( startIdx ) )
202 continue;
203
204 std::vector<size_t> cycle;
205 size_t curIdx = startIdx;
206
207 while( !visited.count( curIdx ) )
208 {
209 visited.insert( curIdx );
210 cycle.push_back( curIdx );
211
212 auto nextIt = desiredTarget.find( curIdx );
213
214 if( nextIt == desiredTarget.end() || nextIt->second == curIdx )
215 break;
216
217 curIdx = nextIt->second;
218 }
219
220 if( cycle.size() < 2 )
221 continue;
222
223 for( size_t i = cycle.size() - 1; i > 0; --i )
224 {
225 size_t firstIdx = cycle[i - 1];
226 size_t secondIdx = cycle[i];
227
228 plan.m_steps.push_back( { firstIdx, secondIdx,
229 aCandidates[firstIdx].m_currentUnit,
230 aCandidates[secondIdx].m_currentUnit } );
231 plan.m_swappedCandidateIndices.insert( firstIdx );
232 plan.m_swappedCandidateIndices.insert( secondIdx );
233 }
234 }
235
236 return plan;
237}
238
239
240BACK_ANNOTATE::BACK_ANNOTATE( SCH_EDIT_FRAME* aFrame, REPORTER& aReporter, bool aRelinkFootprints,
241 bool aProcessFootprints, bool aProcessValues, bool aProcessReferences,
242 bool aProcessNetNames, bool aProcessAttributes, bool aProcessOtherFields,
243 bool aPreferUnitSwaps, bool aPreferPinSwaps, bool aDryRun ) :
244 m_reporter( aReporter ),
245 m_matchByReference( aRelinkFootprints ),
246 m_processFootprints( aProcessFootprints ),
247 m_processValues( aProcessValues ),
248 m_processReferences( aProcessReferences ),
249 m_processNetNames( aProcessNetNames ),
250 m_processAttributes( aProcessAttributes ),
251 m_processOtherFields( aProcessOtherFields ),
252 m_preferUnitSwaps( aPreferUnitSwaps ),
253 m_preferPinSwaps( aPreferPinSwaps ),
254 m_dryRun( aDryRun ),
255 m_frame( aFrame ),
256 m_changesCount( 0 )
257{ }
258
259
260bool BACK_ANNOTATE::BackAnnotateSymbols( const std::string& aNetlist )
261{
262 m_changesCount = 0;
263
266 {
267 m_reporter.ReportTail( _( "Select at least one property to back annotate." ), RPT_SEVERITY_ERROR );
268 return false;
269 }
270
271 getPcbModulesFromString( aNetlist );
272
273 SCH_SHEET_LIST sheets = m_frame->Schematic().Hierarchy();
276
279
281 return true;
282}
283
284
285bool BACK_ANNOTATE::FetchNetlistFromPCB( std::string& aNetlist )
286{
287 if( Kiface().IsSingle() )
288 {
289 DisplayErrorMessage( m_frame, _( "Cannot fetch PCB netlist because Schematic Editor is opened in "
290 "stand-alone mode.\n"
291 "You must launch the KiCad project manager and create a project." ) );
292 return false;
293 }
294
295 KIWAY_PLAYER* frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, false );
296
297 if( !frame )
298 {
299 wxFileName fn( m_frame->Prj().GetProjectFullName() );
300 fn.SetExt( FILEEXT::PcbFileExtension );
301
302 frame = m_frame->Kiway().Player( FRAME_PCB_EDITOR, true );
303 frame->OpenProjectFiles( std::vector<wxString>( 1, fn.GetFullPath() ) );
304 }
305
306 m_frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_PCB_GET_NETLIST, aNetlist );
307 return true;
308}
309
310
312{
313 std::string nullPayload;
314
315 m_frame->Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_PCB_UPDATE_LINKS, nullPayload );
316}
317
318
319void BACK_ANNOTATE::getPcbModulesFromString( const std::string& aPayload )
320{
321 auto getStr = []( const PTREE& pt ) -> wxString
322 {
323 return UTF8( pt.front().first );
324 };
325
326 DSNLEXER lexer( aPayload, From_UTF8( __func__ ) );
327 PTREE doc;
328
329 // NOTE: KiCad's PTREE scanner constructs a property *name* tree, not a property tree.
330 // Every token in the s-expr is stored as a property name; the property's value is then
331 // either the nested s-exprs or an empty PTREE; there are *no* literal property values.
332
333 Scan( &doc, &lexer );
334
335 PTREE& tree = doc.get_child( "pcb_netlist" );
336 wxString msg;
337 m_pcbFootprints.clear();
338
339 for( const std::pair<const std::string, PTREE>& item : tree )
340 {
341 wxString path, value, footprint;
342 bool dnp = false, exBOM = false, exSim = false, exPosFiles = false;
343 std::map<wxString, wxString> pinNetMap, fieldsMap;
344 wxASSERT( item.first == "ref" );
345 wxString ref = getStr( item.second );
346
347 try
348 {
350 path = ref;
351 else
352 path = getStr( item.second.get_child( "timestamp" ) );
353
354 if( path == "" )
355 {
356 msg.Printf( _( "Footprint '%s' has no assigned symbol." ), DescribeRef( ref ) );
357 m_reporter.ReportHead( msg, RPT_SEVERITY_WARNING );
358 continue;
359 }
360
361 footprint = getStr( item.second.get_child( "fpid" ) );
362 value = getStr( item.second.get_child( "value" ) );
363
364 // Get child PTREE of fields
365 boost::optional<const PTREE&> fields = item.second.get_child_optional( "fields" );
366
367 // Parse each field out of the fields string
368 if( fields )
369 {
370 for( const std::pair<const std::string, PTREE>& field : fields.get() )
371 {
372 if( field.first != "field" )
373 continue;
374
375 // Fields are of the format "(field (name "name") "12345")
376 const auto& fieldName = field.second.get_child_optional( "name" );
377 const std::string& fieldValue = field.second.back().first;
378
379 if( !fieldName )
380 continue;
381
382 fieldsMap[getStr( fieldName.get() )] = wxString::FromUTF8( fieldValue );
383 }
384 }
385
386
387 // Get DNP and exclusion attributes out of the properties if they exist
388 for( const auto& child : item.second )
389 {
390 if( child.first != "property" )
391 continue;
392
393 auto property = child.second;
394 auto name = property.get_child_optional( "name" );
395
396 if( !name )
397 continue;
398
399 if( name.get().front().first == "dnp" )
400 dnp = true;
401 else if( name.get().front().first == "exclude_from_bom" )
402 exBOM = true;
403 else if( name.get().front().first == "exclude_from_sim" )
404 exSim = true;
405 else if( name.get().front().first == "exclude_from_pos_files" )
406 exPosFiles = true;
407 }
408
409 boost::optional<const PTREE&> nets = item.second.get_child_optional( "nets" );
410
411 if( nets )
412 {
413 for( const std::pair<const std::string, PTREE>& pin_net : nets.get() )
414 {
415 wxASSERT( pin_net.first == "pin_net" );
416 wxString pinNumber = UTF8( pin_net.second.front().first );
417 wxString netName = UTF8( pin_net.second.back().first );
418 pinNetMap[ pinNumber ] = netName;
419 }
420 }
421 }
422 catch( ... )
423 {
424 wxLogWarning( "Cannot parse PCB netlist for back-annotation." );
425 }
426
427 // Use lower_bound for not to iterate over map twice
428 auto nearestItem = m_pcbFootprints.lower_bound( path );
429
430 if( nearestItem != m_pcbFootprints.end() && nearestItem->first == path )
431 {
432 // Module with this path already exists - generate error
433 msg.Printf( _( "Footprints '%s' and '%s' linked to same symbol." ),
434 DescribeRef( nearestItem->second->m_ref ),
435 DescribeRef( ref ) );
436 m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
437 }
438 else
439 {
440 // Add footprint to the map
441 std::shared_ptr<PCB_FP_DATA> data = std::make_shared<PCB_FP_DATA>(
442 ref, footprint, value, dnp, exBOM, exSim, exPosFiles, pinNetMap, fieldsMap );
443 m_pcbFootprints.insert( nearestItem, std::make_pair( path, data ) );
444 }
445 }
446}
447
448
450{
451 for( const auto& [pcbPath, pcbData] : m_pcbFootprints )
452 {
453 int refIndex;
454 bool foundInMultiunit = false;
455
456 for( const auto& [_, refList] : m_multiUnitsRefs )
457 {
459 refIndex = refList.FindRef( pcbPath );
460 else
461 refIndex = refList.FindRefByFullPath( pcbPath );
462
463 if( refIndex >= 0 )
464 {
465 // If footprint linked to multi unit symbol, we add all symbol's units to
466 // the change list
467 foundInMultiunit = true;
468
469 for( size_t i = 0; i < refList.GetCount(); ++i )
470 {
471 refList[ i ].GetSymbol()->ClearFlags(SKIP_STRUCT );
472 m_changelist.emplace_back( CHANGELIST_ITEM( refList[i], pcbData ) );
473 }
474
475 break;
476 }
477 }
478
479 if( foundInMultiunit )
480 continue;
481
483 refIndex = m_refs.FindRef( pcbPath );
484 else
485 refIndex = m_refs.FindRefByFullPath( pcbPath );
486
487 if( refIndex >= 0 )
488 {
489 m_refs[ refIndex ].GetSymbol()->ClearFlags( SKIP_STRUCT );
490 m_changelist.emplace_back( CHANGELIST_ITEM( m_refs[refIndex], pcbData ) );
491 }
492 else
493 {
494 // Haven't found linked symbol in multiunits or common refs. Generate error
495 m_reporter.ReportTail( wxString::Format( _( "Cannot find symbol for footprint '%s'." ),
496 DescribeRef( pcbData->m_ref ) ),
498 }
499 }
500}
501
503{
504 m_refs.SortByTimeStamp();
505
506 std::sort( m_changelist.begin(), m_changelist.end(),
507 []( const CHANGELIST_ITEM& a, const CHANGELIST_ITEM& b )
508 {
509 return SCH_REFERENCE_LIST::sortByTimeStamp( a.first, b.first );
510 } );
511
512 size_t i = 0;
513
514 for( const std::pair<SCH_REFERENCE, std::shared_ptr<PCB_FP_DATA>>& item : m_changelist )
515 {
516 // Refs and changelist are both sorted by paths, so we just go over m_refs and
517 // generate errors before we will find m_refs member to which item linked
518 while( i < m_refs.GetCount() && m_refs[i].GetPath() != item.first.GetPath() )
519 {
520 const SCH_REFERENCE& ref = m_refs[i];
521
522 if( ref.GetSymbol()->GetExcludedFromBoard() )
523 {
524 m_reporter.ReportTail( wxString::Format( _( "Footprint '%s' is not present on PCB. "
525 "Corresponding symbols in schematic must be "
526 "manually deleted (if desired)." ),
527 DescribeRef( m_refs[i].GetRef() ) ),
529 }
530
531 ++i;
532 }
533
534 ++i;
535 }
536
537 if( m_matchByReference && !m_frame->ReadyToNetlist( _( "Re-linking footprints requires a fully "
538 "annotated schematic." ) ) )
539 {
540 m_reporter.ReportTail( _( "Footprint re-linking canceled by user." ), RPT_SEVERITY_ERROR );
541 }
542}
543
544
546{
547 SCH_COMMIT commit( m_frame );
548 wxString msg;
549
550 // Staged edits hide published nets on their screens; read every net from the pre-edit publication
551 std::optional<SCH_CONNECTIVITY::PUBLICATION_HOLD> hold( std::in_place, m_frame->Schematic().Connectivity() );
552
553 std::set<CHANGELIST_ITEM*> unitSwapItems;
554
555 // First, optionally handle unit swaps across multi-unit symbols where possible
556 // This needs to happen before the rest of the normal changelist processing,
557 // because the swaps will modify the changelist
559 {
560 const wxString variantName = m_frame->Schematic().GetCurrentVariant();
561 REPORTER& debugReporter = NULL_REPORTER::GetInstance(); // Change to m_reporter for debugging
562
563 // Group changelist items by shared PCB footprint data pointer
564 std::map<std::shared_ptr<PCB_FP_DATA>, std::vector<CHANGELIST_ITEM*>> changesPerFp;
565
566 msg.Printf( wxT( "DEBUG(unit-swap): grouped changelist into %zu footprint(s) for unit-swap processing:" ),
567 changesPerFp.size() );
568
569 // The changelist will have multiple entries for multi-unit symbols, each unit ref, e.g. U1A, U1B
570 // will point to the same PCB_FP_DATA. Grouping by pointer allows us to handle all units of a symbol
571 // together by working on all changes to the same PCB footprint at once.
572 for( CHANGELIST_ITEM& item : m_changelist )
573 {
574 changesPerFp[item.second].push_back( &item );
575 msg += wxT( " " ) + item.first.GetRef();
576 }
577
578 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
579
580 // Handle all changes per footprint
581 for( auto& fpChangelistPair : changesPerFp )
582 {
583 std::set<SCH_SYMBOL*> swappedSymbols;
584 std::set<CHANGELIST_ITEM*> swappedItems;
585 std::shared_ptr<PCB_FP_DATA> fp = fpChangelistPair.first;
586 auto& changedFpItems = fpChangelistPair.second;
587
588 // Build symbol unit list for this footprint (multi-unit candidates)
589 // Snapshot of one schematic unit instance (ref + sheet path + nets) for matching
590 struct SYM_UNIT
591 {
592 SCH_SYMBOL* sym = nullptr;
593 SCH_SCREEN* screen = nullptr;
594 const SCH_SHEET_PATH* sheetPath = nullptr;
595 wxString ref;
596 int currentUnit = 0;
597 // Track these so we avoid re-applying label/field changes to swapped units
598 CHANGELIST_ITEM* changeItem = nullptr;
599 std::map<wxString, wxString> schNetsByPin; // pinNumber -> net (schematic)
600 std::map<wxString, wxString> pcbNetsByPin; // pinNumber -> net (from PCB pin map)
601 std::vector<wxString> unitPinNumbers; // library-defined pin numbers per unit
602 std::vector<wxString> schNetsInUnitOrder;
603 std::vector<wxString> pcbNetsInUnitOrder;
604 };
605
606 // All symbol units for this footprint
607 std::vector<SYM_UNIT> symbolUnits;
608 std::map<wxString, wxString> pcbNetsByLogicalPin;
609 bool mappingValid = true;
610
611 std::map<LIB_SYMBOL*, std::vector<LIB_SYMBOL::UNIT_PIN_INFO>> unitPinsByLibSymbol;
612
613 auto getUnitPins =
614 [&]( SCH_SYMBOL* symbol, int unitNumber ) -> std::vector<wxString>
615 {
616 if( unitNumber <= 0 )
617 return {};
618
619 if( !symbol )
620 return {};
621
622 LIB_SYMBOL* libSymbol = symbol->GetLibSymbolRef().get();
623
624 if( !libSymbol )
625 return {};
626
627 auto found = unitPinsByLibSymbol.find( libSymbol );
628
629 if( found == unitPinsByLibSymbol.end() )
630 found = unitPinsByLibSymbol.emplace( libSymbol, libSymbol->GetUnitPinInfo() ).first;
631
632 const std::vector<LIB_SYMBOL::UNIT_PIN_INFO>& unitInfos = found->second;
633
634 if( unitNumber > static_cast<int>( unitInfos.size() ) )
635 return {};
636
637 return unitInfos[unitNumber - 1].m_pinNumbers;
638 };
639
640 for( CHANGELIST_ITEM* changedItem : changedFpItems )
641 {
642 SCH_REFERENCE& ref = changedItem->first;
643 SCH_SYMBOL* symbol = ref.GetSymbol();
644 SCH_SCREEN* screen = ref.GetSheetPath().LastScreen();
645
646 if( !symbol )
647 continue;
648
649 // Collect nets keyed by pin number. Ordering by XY is intentionally avoided
650 // here; see comment in SYM_UNIT above.
651 SYM_UNIT symbolUnit;
652 symbolUnit.sym = symbol;
653 symbolUnit.screen = screen;
654 symbolUnit.ref = symbol->GetRef( &ref.GetSheetPath(), true );
655 symbolUnit.sheetPath = &ref.GetSheetPath();
656 symbolUnit.changeItem = changedItem;
657
658 int currentUnit = ref.GetUnit();
659
660 if( currentUnit <= 0 )
661 currentUnit = symbol->GetUnitSelection( &ref.GetSheetPath() );
662
663 if( currentUnit <= 0 )
664 currentUnit = symbol->GetUnit();
665
666 symbolUnit.currentUnit = currentUnit;
667 symbolUnit.unitPinNumbers = getUnitPins( symbol, symbolUnit.currentUnit );
668
669 const SCH_SHEET_PATH& sheetPath = ref.GetSheetPath();
670
671 if( !CollectBackannotatePcbNets( *symbol, sheetPath, variantName, fp->m_pinMap, pcbNetsByLogicalPin ) )
672 {
673 mappingValid = false;
674 break;
675 }
676
677 for( SCH_PIN* pin : symbol->GetPins( &sheetPath ) )
678 {
679 const wxString schNet = pin->GetConnectionName( &sheetPath, false, true ).value_or( wxString() );
680
681 for( const wxString& pinNum : pin->GetStackedPinNumbers() )
682 {
683 symbolUnit.pcbNetsByPin[pinNum] = pcbNetsByLogicalPin.at( pinNum );
684 symbolUnit.schNetsByPin[pinNum] = schNet;
685 }
686 }
687
688 symbolUnit.pcbNetsInUnitOrder = netsInUnitOrder( symbolUnit.unitPinNumbers, symbolUnit.pcbNetsByPin );
689 symbolUnit.schNetsInUnitOrder = netsInUnitOrder( symbolUnit.unitPinNumbers, symbolUnit.schNetsByPin );
690
691 symbolUnits.push_back( symbolUnit );
692 }
693
694 auto vectorToString =
695 []( const std::vector<wxString>& values ) -> wxString
696 {
697 return fmt::format( L"{}", fmt::join( values, L", " ) );
698 };
699
700 auto mapToString =
701 [vectorToString]( const std::map<wxString, wxString>& pinMap ) -> wxString
702 {
703 std::vector<wxString> entries;
704
705 for( const std::pair<const wxString, wxString>& pin : pinMap )
706 entries.push_back( pin.first + '=' + pin.second );
707
708 return vectorToString( entries );
709 };
710
711 msg.Printf( wxT( "DEBUG(unit-swap): footprint %s processed (%zu units, dryRun=%d)." ), fp->m_ref,
712 symbolUnits.size(), m_dryRun ? 1 : 0 );
713 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
714
715 // For debugging, sort the symbol units by ref
716 std::sort( symbolUnits.begin(), symbolUnits.end(),
717 []( const SYM_UNIT& a, const SYM_UNIT& b )
718 {
719 return a.ref < b.ref;
720 } );
721
722 for( const SYM_UNIT& su : symbolUnits )
723 {
724 wxString pcbPins = mapToString( su.pcbNetsByPin );
725 wxString schPins = mapToString( su.schNetsByPin );
726 wxString unitPins = vectorToString( su.unitPinNumbers );
727 wxString pcbUnitNetSeq = vectorToString( su.pcbNetsInUnitOrder );
728 wxString schUnitNetSeq = vectorToString( su.schNetsInUnitOrder );
729
730 msg.Printf( wxT( "DEBUG(unit-swap): unit %d: %s pcbPins[%s] schPins[%s] unitPins[%s] pcbUnitNets[%s] "
731 "schUnitNets[%s]." ),
732 su.currentUnit, su.ref, pcbPins, schPins, unitPins, pcbUnitNetSeq, schUnitNetSeq );
733 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
734 }
735
736 if( !mappingValid || symbolUnits.size() < 2 )
737 continue;
738
739 std::vector<BACKANNOTATE_UNIT_SWAP_CANDIDATE> candidates;
740
741 for( const SYM_UNIT& symbolUnit : symbolUnits )
742 {
744 candidate.m_ref = symbolUnit.ref;
745 candidate.m_currentUnit = symbolUnit.currentUnit;
746 candidate.m_schNetsByPin = symbolUnit.schNetsByPin;
747 candidate.m_unitPinNumbers = symbolUnit.unitPinNumbers;
748 candidates.push_back( candidate );
749 }
750
752
753 if( !plan.m_mappingOk )
754 {
755 msg.Printf( wxT( "DEBUG(unit-swap): mapping failed for footprint %s." ), fp->m_ref );
756 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
757 continue;
758 }
759
760 if( plan.m_identity )
761 {
762 msg.Printf( wxT( "DEBUG(unit-swap): footprint %s already aligned (identity mapping)." ), fp->m_ref );
763 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
764 continue;
765 }
766
767 if( !m_dryRun )
768 {
769 for( size_t candidateIdx : plan.m_swappedCandidateIndices )
770 commit.Modify( symbolUnits[candidateIdx].sym, symbolUnits[candidateIdx].screen );
771 }
772
773 for( const BACKANNOTATE_UNIT_SWAP_STEP& step : plan.m_steps )
774 {
775 SYM_UNIT& a = symbolUnits[step.m_firstIndex];
776 SYM_UNIT& b = symbolUnits[step.m_secondIndex];
777 int aUnit = a.currentUnit;
778 int bUnit = b.currentUnit;
779
780 if( !m_dryRun )
781 {
782 a.sym->SetUnit( bUnit );
783 b.sym->SetUnit( aUnit );
784
785 if( const SCH_SHEET_PATH* sheet = a.sheetPath )
786 a.sym->SetUnitSelection( sheet, bUnit );
787
788 if( const SCH_SHEET_PATH* sheet = b.sheetPath )
789 b.sym->SetUnitSelection( sheet, aUnit );
790 }
791
792 a.currentUnit = bUnit;
793 b.currentUnit = aUnit;
794
795 swappedSymbols.insert( a.sym );
796 swappedSymbols.insert( b.sym );
797
798 if( a.changeItem )
799 {
800 swappedItems.insert( a.changeItem );
801 unitSwapItems.insert( a.changeItem );
802 }
803
804 if( b.changeItem )
805 {
806 swappedItems.insert( b.changeItem );
807 unitSwapItems.insert( b.changeItem );
808 }
809
810 wxString baseRef = a.sym->GetRef( a.sheetPath, false );
811 wxString unitAString = a.sym->SubReference( aUnit, false );
812 wxString unitBString = b.sym->SubReference( bUnit, false );
813
814 if( unitAString.IsEmpty() )
815 unitAString.Printf( wxT( "%d" ), aUnit );
816
817 if( unitBString.IsEmpty() )
818 unitBString.Printf( wxT( "%d" ), bUnit );
819
820 msg.Printf( _( "Swap %s unit %s with unit %s." ),
821 DescribeRef( baseRef ),
822 unitAString,
823 unitBString );
824 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
826 }
827
828 msg.Printf( wxT( "DEBUG(unit-swap): applied %zu swap steps for footprint %s." ),
829 plan.m_steps.size(), fp->m_ref );
830 debugReporter.ReportHead( msg, RPT_SEVERITY_INFO );
831
832 // Remove label updates for swapped symbols by marking their SKIP_STRUCT flag
833 if( !m_dryRun )
834 {
835 for( CHANGELIST_ITEM* changedItem : changedFpItems )
836 {
837 SCH_SYMBOL* symbol = changedItem->first.GetSymbol();
838
839 if( !symbol )
840 continue;
841
842 if( swappedItems.count( changedItem ) || swappedSymbols.count( symbol ) )
843 symbol->SetFlags( SKIP_STRUCT );
844
845 int updatedUnit = symbol->GetUnitSelection( &changedItem->first.GetSheetPath() );
846 changedItem->first.SetUnit( updatedUnit );
847 }
848 }
849 }
850 }
851
852 // Apply changes from change list
853 for( CHANGELIST_ITEM& item : m_changelist )
854 {
855 SCH_REFERENCE& ref = item.first;
856 PCB_FP_DATA& fpData = *item.second;
857 SCH_SYMBOL* symbol = ref.GetSymbol();
858 SCH_SCREEN* screen = ref.GetSheetPath().LastScreen();
859 wxString oldFootprint = ref.GetFootprint();
860 wxString oldValue = ref.GetValue();
861 bool oldDNP = ref.GetSymbol()->GetDNP();
862 bool oldExBOM = ref.GetSymbol()->GetExcludedFromBOM();
863 bool oldExSim = ref.GetSymbol()->GetExcludedFromSim();
864 bool oldExPosFiles = ref.GetSymbol()->GetExcludedFromPosFiles();
865 // Skip prevents us from re-applying label/field changes to units we just swapped
866 bool skip = ( ref.GetSymbol()->GetFlags() & SKIP_STRUCT ) > 0 || unitSwapItems.count( &item ) > 0;
867
868 auto boolString =
869 []( bool b ) -> wxString
870 {
871 return b ? _( "true" ) : _( "false" );
872 };
873
874 if( !m_dryRun )
875 commit.Modify( symbol, screen, RECURSE_MODE::NO_RECURSE );
876
877 if( m_processReferences && ref.GetRef() != fpData.m_ref && !skip
878 && !symbol->GetField( FIELD_T::REFERENCE )->HasTextVars() )
879 {
881 msg.Printf( _( "Change %s reference designator to '%s'." ),
882 DescribeRef( ref.GetRef() ),
883 fpData.m_ref );
884
885 if( !m_dryRun )
886 symbol->SetRef( &ref.GetSheetPath(), fpData.m_ref );
887
888 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
889 }
890
891 if( m_processFootprints && oldFootprint != fpData.m_footprint && !skip
892 && !symbol->GetField( FIELD_T::FOOTPRINT )->HasTextVars() )
893 {
895 msg.Printf( _( "Change %s footprint assignment from '%s' to '%s'." ),
896 DescribeRef( ref.GetRef() ),
897 EscapeHTML( oldFootprint ),
898 EscapeHTML( fpData.m_footprint ) );
899
900 if( !m_dryRun )
901 symbol->SetFootprintFieldText( fpData.m_footprint );
902
903 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
904 }
905
906 if( m_processValues && oldValue != fpData.m_value && !skip
907 && !symbol->GetField( FIELD_T::VALUE )->HasTextVars() )
908 {
910 msg.Printf( _( "Change %s value from '%s' to '%s'." ),
911 DescribeRef( ref.GetRef() ),
912 EscapeHTML( oldValue ),
913 EscapeHTML( fpData.m_value ) );
914
915 if( !m_dryRun )
916 symbol->SetValueFieldText( fpData.m_value );
917
918 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
919 }
920
921 if( m_processAttributes && oldDNP != fpData.m_DNP && !skip )
922 {
924 msg.Printf( _( "Change %s 'Do not populate' from '%s' to '%s'." ),
925 DescribeRef( ref.GetRef() ),
926 boolString( oldDNP ),
927 boolString( fpData.m_DNP ) );
928
929 if( !m_dryRun )
930 symbol->SetDNP( fpData.m_DNP );
931
932 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
933 }
934
935 if( m_processAttributes && oldExBOM != fpData.m_excludeFromBOM && !skip )
936 {
938 msg.Printf( _( "Change %s 'Exclude from bill of materials' from '%s' to '%s'." ),
939 DescribeRef( ref.GetRef() ),
940 boolString( oldExBOM ),
941 boolString( fpData.m_excludeFromBOM ) );
942
943 if( !m_dryRun )
944 symbol->SetExcludedFromBOM( fpData.m_excludeFromBOM );
945
946 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
947 }
948
949 if( m_processAttributes && oldExSim != fpData.m_excludeFromSim && !skip )
950 {
952 msg.Printf( _( "Change %s 'Exclude from simulation' from '%s' to '%s'." ),
953 DescribeRef( ref.GetRef() ),
954 boolString( oldExSim ),
955 boolString( fpData.m_excludeFromSim ) );
956
957 if( !m_dryRun )
958 symbol->SetExcludedFromSim( fpData.m_excludeFromSim );
959
960 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
961 }
962
963 if( m_processAttributes && oldExPosFiles != fpData.m_excludeFromPosFiles && !skip )
964 {
966 msg.Printf( _( "Change %s 'Exclude from position files' from '%s' to '%s'." ), DescribeRef( ref.GetRef() ),
967 boolString( oldExPosFiles ), boolString( fpData.m_excludeFromPosFiles ) );
968
969 if( !m_dryRun )
971
972 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
973 }
974
975 std::set<wxString> swappedPins;
976
977 // Try to satisfy footprint pad net swaps by moving symbol pins before falling back to labels.
978 if( m_preferPinSwaps && m_processNetNames && !skip )
979 swappedPins = applyPinSwaps( symbol, ref, fpData, &commit );
980
981 if( m_processNetNames && !skip )
982 {
983 for( const std::pair<const wxString, wxString>& entry : fpData.m_pinMap )
984 {
985 const wxString& pinNumber = entry.first;
986 const wxString& shortNetName = entry.second;
987 SCH_PIN* pin = symbol->GetPin( pinNumber );
988
989 // Skip pins that the user preferred to handle with pin swaps in applyPreferredPinSwaps()
990 if( swappedPins.count( pinNumber ) > 0 )
991 continue;
992
993 if( !pin )
994 {
995 msg.Printf( _( "Cannot find %s pin '%s'." ),
996 DescribeRef( ref.GetRef() ),
997 EscapeHTML( pinNumber ) );
998 m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
999
1000 continue;
1001 }
1002
1003 const std::optional<wxString> netName = pin->GetConnectionName( &ref.GetSheetPath(), false, true );
1004
1005 if( netName && *netName != shortNetName )
1006 processNetNameChange( &commit, ref.GetRef(), pin, ref.GetSheetPath(), *netName, shortNetName );
1007 }
1008 }
1009
1011 {
1012 // Need to handle three cases: existing field, new field, deleted field
1013 for( const std::pair<const wxString, wxString>& field : fpData.m_fieldsMap )
1014 {
1015 const wxString& fpFieldName = field.first;
1016 const wxString& fpFieldValue = field.second;
1017 SCH_FIELD* symField = symbol->GetField( fpFieldName );
1018
1019 // Skip fields that are individually controlled
1021 || fpFieldName == GetDefaultFieldName( FIELD_T::VALUE, UNTRANSLATED ) )
1022 {
1023 continue;
1024 }
1025
1026 // 1. Existing fields has changed value
1027 // PCB Field value is checked against the shown text because this is the value
1028 // with all the variables resolved. The footprints field value gets the symbol's
1029 // resolved value when the PCB is updated from the schematic.
1030 if( symField
1031 && !symField->HasTextVars()
1032 && symField->GetShownText( &ref.GetSheetPath(), INTERNAL ) != fpFieldValue )
1033 {
1035 msg.Printf( _( "Change %s field '%s' value to '%s'." ),
1036 DescribeRef( ref.GetRef() ),
1037 EscapeHTML( symField->GetUntranslatedName() ),
1038 EscapeHTML( fpFieldValue ) );
1039
1040 if( !m_dryRun )
1041 symField->SetText( fpFieldValue );
1042
1043 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1044 }
1045
1046 // 2. New field has been added to footprint and needs to be added to symbol
1047 if( symField == nullptr )
1048 {
1050 msg.Printf( _( "Add %s field '%s' with value '%s'." ),
1051 DescribeRef( ref.GetRef() ),
1052 EscapeHTML( fpFieldName ),
1053 EscapeHTML( fpFieldValue ) );
1054
1055 if( !m_dryRun )
1056 {
1057 SCH_FIELD newField( symbol, FIELD_T::USER, fpFieldName );
1058 newField.SetText( fpFieldValue );
1059 newField.SetTextPos( symbol->GetPosition() );
1060 newField.SetVisible( false ); // Don't clutter up the schematic
1061 symbol->AddField( newField );
1062 }
1063
1064 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1065 }
1066 }
1067
1068 // 3. Existing field has been deleted from footprint and needs to be deleted from symbol
1069 // Check all symbol fields for existence in the footprint field map.
1070 // Collect names first to avoid iterator invalidation when removing fields.
1071 std::vector<wxString> fieldsToDelete;
1072
1073 for( SCH_FIELD& field : symbol->GetFields() )
1074 {
1075 if( field.IsMandatory() )
1076 continue;
1077
1078 if( fpData.m_fieldsMap.find( field.GetUntranslatedName() ) == fpData.m_fieldsMap.end() )
1079 {
1081 msg.Printf( _( "Delete %s field '%s.'" ),
1082 DescribeRef( ref.GetRef() ),
1083 EscapeHTML( field.GetName() ) );
1084
1085 fieldsToDelete.push_back( field.GetName() );
1086 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1087 }
1088 }
1089
1090 if( !m_dryRun )
1091 {
1092 for( const wxString& fieldName : fieldsToDelete )
1093 symbol->RemoveField( fieldName );
1094 }
1095 }
1096
1097 if( symbol->GetFlags() & SKIP_STRUCT )
1098 symbol->ClearFlags( SKIP_STRUCT );
1099
1100 unitSwapItems.erase( &item );
1101
1102 // TODO: back-annotate netclass changes?
1103 }
1104
1105 hold.reset();
1106
1107 if( !m_dryRun )
1108 {
1109 m_frame->RecalculateConnections( &commit, NO_CLEANUP );
1110 m_frame->UpdateNetHighlightStatus();
1111
1112 commit.Push( _( "Update Schematic from PCB" ) );
1113 }
1114}
1115
1116
1118{
1120
1121 // Initial orientation from the pin
1122 switch( aPin->GetLibPin()->GetOrientation() )
1123 {
1124 default:
1125 case PIN_ORIENTATION::PIN_RIGHT: spin = SPIN_STYLE::LEFT; break;
1126 case PIN_ORIENTATION::PIN_UP: spin = SPIN_STYLE::BOTTOM; break;
1127 case PIN_ORIENTATION::PIN_DOWN: spin = SPIN_STYLE::UP; break;
1128 case PIN_ORIENTATION::PIN_LEFT: spin = SPIN_STYLE::RIGHT; break;
1129 }
1130
1131 // Reorient based on the actual symbol orientation now
1132 struct ORIENT
1133 {
1134 int flag;
1135 int n_rots;
1136 int mirror_x;
1137 int mirror_y;
1138 }
1139 orientations[] =
1140 {
1141 { SYM_ORIENT_0, 0, 0, 0 },
1142 { SYM_ORIENT_90, 1, 0, 0 },
1143 { SYM_ORIENT_180, 2, 0, 0 },
1144 { SYM_ORIENT_270, 3, 0, 0 },
1145 { SYM_MIRROR_X + SYM_ORIENT_0, 0, 1, 0 },
1146 { SYM_MIRROR_X + SYM_ORIENT_90, 1, 1, 0 },
1147 { SYM_MIRROR_Y, 0, 0, 1 },
1148 { SYM_MIRROR_X + SYM_ORIENT_270, 3, 1, 0 },
1149 { SYM_MIRROR_Y + SYM_ORIENT_0, 0, 0, 1 },
1150 { SYM_MIRROR_Y + SYM_ORIENT_90, 1, 0, 1 },
1151 { SYM_MIRROR_Y + SYM_ORIENT_180, 2, 0, 1 },
1152 { SYM_MIRROR_Y + SYM_ORIENT_270, 3, 0, 1 }
1153 };
1154
1155 ORIENT o = orientations[ 0 ];
1156
1157 const SCH_SYMBOL* parentSymbol = static_cast<const SCH_SYMBOL*>( aPin->GetParentSymbol() );
1158
1159 if( !parentSymbol )
1160 return spin;
1161
1162 int symbolOrientation = parentSymbol->GetOrientation();
1163
1164 for( const ORIENT& i : orientations )
1165 {
1166 if( i.flag == symbolOrientation )
1167 {
1168 o = i;
1169 break;
1170 }
1171 }
1172
1173 for( int i = 0; i < o.n_rots; i++ )
1174 spin = spin.RotateCCW();
1175
1176 if( o.mirror_x )
1177 spin = spin.MirrorX();
1178
1179 if( o.mirror_y )
1180 spin = spin.MirrorY();
1181
1182 return spin;
1183}
1184
1185
1186void addConnections( SCH_ITEM* aItem, const SCH_SHEET_PATH& aSheetPath, std::set<SCH_ITEM*>& connectedItems )
1187{
1188 if( !connectedItems.insert( aItem ).second )
1189 return;
1190
1191 std::vector<SCH_ITEM*> neighbors;
1192 SCHEMATIC* schematic = aItem->Schematic();
1193
1194 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine && schematic )
1195 {
1196 if( const auto connection = schematic->Connectivity().Connection( aItem->m_Uuid, aSheetPath.PathRef() ) )
1197 neighbors = connection->ConnectedItems();
1198 }
1199 else
1200 {
1201 neighbors = aItem->ConnectedItems( aSheetPath );
1202 }
1203
1204 for( SCH_ITEM* connectedItem : neighbors )
1205 addConnections( connectedItem, aSheetPath, connectedItems );
1206}
1207
1208
1209std::set<wxString> BACK_ANNOTATE::applyPinSwaps( SCH_SYMBOL* aSymbol, const SCH_REFERENCE& aReference,
1210 const PCB_FP_DATA& aFpData, SCH_COMMIT* aCommit )
1211{
1212 // Tracks pin numbers that we end up swapping so that the caller can skip any
1213 // duplicate label-only handling for those pins.
1214 std::set<wxString> swappedPins;
1215
1216 if( !aSymbol )
1217 return swappedPins;
1218
1219 SCH_SCREEN* screen = aReference.GetSheetPath().LastScreen();
1220
1221 if( !screen )
1222 return swappedPins;
1223
1224 wxCHECK( m_frame, swappedPins );
1225
1226 // Used to build the list of schematic pins whose current net assignment does not match the PCB.
1227 struct PIN_CHANGE
1228 {
1229 SCH_PIN* pin;
1230 wxString pinNumber;
1231 wxString currentNet;
1232 wxString targetNet;
1233 };
1234
1235 std::vector<PIN_CHANGE> mismatches;
1236
1237 // Helper map that lets us find all pins currently on a given net.
1238 std::map<wxString, std::vector<size_t>> pinsByCurrentNet;
1239
1240 // Build the mismatch list by inspecting each footprint pin that differs from the schematic.
1241 for( const std::pair<const wxString, wxString>& entry : aFpData.m_pinMap )
1242 {
1243 const wxString& pinNumber = entry.first;
1244 const wxString& desiredNet = entry.second;
1245
1246 SCH_PIN* pin = aSymbol->GetPin( pinNumber );
1247
1248 // Ignore power pins and anything marked as non-connectable. Power pins can map to
1249 // hidden/global references, e.g. implicit power connections on logic symbols.
1250 // KiCad pins are currently always connectable, but the extra guard keeps the
1251 // logic robust if alternate pin types (e.g. explicit mechanical/NC pins)
1252 // ever start reporting false.
1253 if( !pin || pin->IsPower() || !pin->IsConnectable() )
1254 continue;
1255
1256 wxString currentNet =
1257 pin->GetConnectionName( &aReference.GetSheetPath(), false, true ).value_or( wxString() );
1258
1259 if( desiredNet.IsEmpty() || currentNet.IsEmpty() )
1260 continue;
1261
1262 if( desiredNet == currentNet )
1263 continue;
1264
1265 size_t idx = mismatches.size();
1266 mismatches.push_back( { pin, pinNumber, currentNet, desiredNet } );
1267 pinsByCurrentNet[currentNet].push_back( idx );
1268 }
1269
1270 if( mismatches.size() < 2 )
1271 return swappedPins;
1272
1273 // Track which mismatch entries we have already consumed, and the underlying pin objects
1274 // we still need to clean up wiring around once the geometry swap is done.
1275 std::vector<bool> handled( mismatches.size(), false );
1276 std::vector<SCH_PIN*> swappedPinObjects;
1277 bool swappedLibPins = false;
1278 wxString msg;
1279
1280 bool allowPinSwaps = false;
1281 wxString currentProjectName = m_frame->Prj().GetProjectName();
1282
1283 if( m_frame->eeconfig() )
1284 allowPinSwaps = m_frame->eeconfig()->m_Input.allow_unconstrained_pin_swaps;
1285
1286 std::set<wxString> sharedSheetPaths;
1287 std::set<wxString> sharedProjectNames;
1288 bool sharedSheetSymbol = SymbolHasSheetInstances( *aSymbol, currentProjectName,
1289 &sharedSheetPaths, &sharedProjectNames );
1290
1291 std::set<wxString> friendlySheetNames;
1292
1293 if( sharedSheetSymbol && !sharedSheetPaths.empty() )
1294 friendlySheetNames = GetSheetNamesFromPaths( sharedSheetPaths, m_frame->Schematic() );
1295
1296 // Check each mismatch and try to find a partner whose desired net matches our current net
1297 // (i.e. the two pins have been swapped on the PCB).
1298 for( size_t i = 0; i < mismatches.size(); ++i )
1299 {
1300 // Skip entries already handled by a previous successful swap.
1301 if( handled[i] )
1302 continue;
1303
1304 PIN_CHANGE& change = mismatches[i];
1305
1306 // Find candidate pins whose current net equals the net we want to move this pin to.
1307 auto range = pinsByCurrentNet.find( change.targetNet );
1308
1309 // Nobody currently on the desired net, so there’s no reciprocal swap to apply.
1310 if( range == pinsByCurrentNet.end() )
1311 continue;
1312
1313 // Track the best partner index in case we discover a reciprocal mismatch below.
1314 size_t partnerIdx = std::numeric_limits<size_t>::max();
1315
1316 // Examine every pin that presently lives on the net we want to move to.
1317 for( size_t candidateIdx : range->second )
1318 {
1319 if( candidateIdx == i || handled[candidateIdx] )
1320 continue;
1321
1322 PIN_CHANGE& candidate = mismatches[candidateIdx];
1323
1324 // Potential swap partner must want to move to our current net, i.e. the PCB swapped the
1325 // two nets between these pins.
1326 if( candidate.targetNet == change.currentNet )
1327 {
1328 partnerIdx = candidateIdx;
1329 break;
1330 }
1331 }
1332
1333 // No viable partner found; either there was no reciprocal net mismatch or it was already
1334 // consumed. Leave this entry for label-based handling.
1335 if( partnerIdx == std::numeric_limits<size_t>::max() )
1336 continue;
1337
1338 PIN_CHANGE& partner = mismatches[partnerIdx];
1339
1340 // Sanity check: both pins must belong to the same schematic symbol before we swap geometry;
1341 // this prevents us from moving pin outlines between different units of a multi-unit symbol.
1342 if( change.pin->GetParentSymbol() != partner.pin->GetParentSymbol() )
1343 continue;
1344
1345 if( !allowPinSwaps || sharedSheetSymbol )
1346 {
1347 if( !sharedProjectNames.empty() )
1348 {
1349 std::vector<wxString> otherProjects;
1350
1351 for( const wxString& name : sharedProjectNames )
1352 {
1353 if( !currentProjectName.IsEmpty() && name.IsSameAs( currentProjectName ) )
1354 continue;
1355
1356 otherProjects.push_back( name );
1357 }
1358
1359 wxString projects = AccumulateDescriptions( otherProjects );
1360
1361 if( projects.IsEmpty() )
1362 {
1363 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but the symbol is shared across other projects." ),
1364 DescribeRef( aReference.GetRef() ),
1365 EscapeHTML( change.pin->GetShownNumber() ),
1366 EscapeHTML( partner.pin->GetShownNumber() ) );
1367 }
1368 else
1369 {
1370 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but the symbol is shared across other "
1371 "projects (%s)." ),
1372 aReference.GetRef(), EscapeHTML( change.pin->GetShownNumber() ),
1373 EscapeHTML( partner.pin->GetShownNumber() ), projects );
1374 }
1375 }
1376 else if( !friendlySheetNames.empty() )
1377 {
1378 wxString sheets = AccumulateDescriptions( friendlySheetNames );
1379
1380 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but the symbol is used by multiple sheet "
1381 "instances (%s)." ),
1382 DescribeRef( aReference.GetRef() ),
1383 EscapeHTML( change.pin->GetShownNumber() ),
1384 EscapeHTML( partner.pin->GetShownNumber() ), sheets );
1385 }
1386 else if( sharedSheetSymbol )
1387 {
1388 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but the symbol is shared." ),
1389 DescribeRef( aReference.GetRef() ),
1390 EscapeHTML( change.pin->GetShownNumber() ),
1391 EscapeHTML( partner.pin->GetShownNumber() ) );
1392 }
1393 else
1394 {
1395 msg.Printf( _( "Would swap %s pins %s and %s to match PCB, but unconstrained pin swaps are disabled in "
1396 "the schematic preferences." ),
1397 DescribeRef( aReference.GetRef() ),
1398 EscapeHTML( change.pin->GetShownNumber() ),
1399 EscapeHTML( partner.pin->GetShownNumber() ) );
1400 }
1401 m_reporter.ReportHead( msg, RPT_SEVERITY_INFO );
1402
1403 handled[i] = true;
1404 handled[partnerIdx] = true;
1405 continue;
1406 }
1407
1408 if( !m_dryRun )
1409 {
1410 wxCHECK2( aCommit, continue );
1411
1412 // Record the two pins in the commit and physically swap their local geometry.
1413 aCommit->Modify( change.pin, screen, RECURSE_MODE::RECURSE );
1414 aCommit->Modify( partner.pin, screen, RECURSE_MODE::RECURSE );
1415
1416 swappedLibPins |= SwapPinGeometry( change.pin, partner.pin );
1417 }
1418
1419 // Don’t pick either entry again for another pairing.
1420 handled[i] = true;
1421 handled[partnerIdx] = true;
1422
1423 // Remember which pin numbers we touched so the caller can suppress duplicate work.
1424 swappedPins.insert( change.pinNumber );
1425 swappedPins.insert( partner.pinNumber );
1426 swappedPinObjects.push_back( change.pin );
1427 swappedPinObjects.push_back( partner.pin );
1428
1430
1431 msg.Printf( _( "Swap %s pins %s and %s to match PCB." ),
1432 DescribeRef( aReference.GetRef() ),
1433 EscapeHTML( change.pin->GetShownNumber() ),
1434 EscapeHTML( partner.pin->GetShownNumber() ) );
1435 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1436 }
1437
1438 // Nothing left to do if we didn't find a valid swap pair when we were in dry-run mode.
1439 if( swappedPinObjects.empty() )
1440 return swappedPins;
1441
1442 if( !m_dryRun )
1443 {
1444 if( swappedLibPins )
1445 aSymbol->UpdatePins();
1446
1447 m_frame->UpdateItem( aSymbol, false, true );
1448
1449 if( TOOL_MANAGER* toolMgr = m_frame->GetToolManager() )
1450 {
1451 if( SCH_LINE_WIRE_BUS_TOOL* lwbTool = toolMgr->GetTool<SCH_LINE_WIRE_BUS_TOOL>() )
1452 {
1453 SCH_SELECTION cleanupSelection( screen );
1454
1455 // Make sure we tidy up any wires connected to the pins whose geometry just moved.
1456 for( SCH_PIN* swappedPin : swappedPinObjects )
1457 cleanupSelection.Add( swappedPin );
1458
1459 lwbTool->TrimOverLappingWires( aCommit, &cleanupSelection );
1460 lwbTool->AddJunctionsIfNeeded( aCommit, &cleanupSelection );
1461 }
1462 }
1463
1464 m_frame->Schematic().CleanUp( aCommit );
1465 }
1466
1467 return swappedPins;
1468}
1469
1470
1471void BACK_ANNOTATE::processNetNameChange( SCH_COMMIT* aCommit, const wxString& aRef, SCH_PIN* aPin,
1472 const SCH_SHEET_PATH& aSheet,
1473 const wxString& aOldName, const wxString& aNewName )
1474{
1475 wxString msg;
1476
1477 // Find a physically-connected driver. We can't use the SCH_CONNECTION's m_driver because
1478 // it has already been resolved by merging subgraphs with the same label, etc., and our
1479 // name change may cause that resolution to change.
1480
1481 std::set<SCH_ITEM*> connectedItems;
1482 SCH_ITEM* driver = nullptr;
1484
1485 addConnections( aPin, aSheet, connectedItems );
1486
1487 for( SCH_ITEM* item : connectedItems )
1488 {
1490
1491 if( priority > driverPriority )
1492 {
1493 driver = item;
1494 driverPriority = priority;
1495 }
1496 }
1497
1498 switch( driver->Type() )
1499 {
1500 case SCH_LABEL_T:
1501 case SCH_GLOBAL_LABEL_T:
1502 case SCH_HIER_LABEL_T:
1503 case SCH_SHEET_PIN_T:
1505
1506 msg.Printf( _( "Change %s pin %s net label from '%s' to '%s'." ),
1507 DescribeRef( aRef ),
1508 EscapeHTML( aPin->GetShownNumber() ),
1509 EscapeHTML( aOldName ),
1510 EscapeHTML( aNewName ) );
1511
1512 if( !m_dryRun )
1513 {
1514 aCommit->Modify( driver, aSheet.LastScreen() );
1515 static_cast<SCH_LABEL_BASE*>( driver )->SetText( aNewName );
1516 }
1517
1518 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1519 break;
1520
1521 case SCH_PIN_T:
1522 if( static_cast<SCH_PIN*>( driver )->IsPower() )
1523 {
1524 msg.Printf( _( "Net %s cannot be changed to %s because it is driven by a power pin." ),
1525 EscapeHTML( aOldName ),
1526 EscapeHTML( aNewName ) );
1527
1528 m_reporter.ReportHead( msg, RPT_SEVERITY_ERROR );
1529 break;
1530 }
1531
1532 // The physical connection walk could not find a label driver. This can happen when
1533 // a label sits in the middle of an unsplit wire. Fall back to the connection graph's
1534 // resolved driver which handles this case through subgraph merging.
1535 {
1536 SCH_ITEM* resolvedDriver = nullptr;
1537 SCH_SHEET_PATH driverSheet;
1539 SCHEMATIC& schematic = m_frame->Schematic();
1540
1541 if( ADVANCED_CFG::GetCfg().m_ConnectivityEngine )
1542 {
1543 const auto net = schematic.Connectivity().GetSubgraphForItem( aPin->m_Uuid, aSheet.PathRef() );
1544 const auto path = net ? schematic.Hierarchy().GetSheetPathByKIIDPath( net->Sheet() )
1545 : std::optional<SCH_SHEET_PATH>();
1546
1547 if( path && net->Driver() )
1548 {
1549 resolvedDriver = net->Driver();
1550 driverSheet = *path;
1551 resolvedPriority = CONNECTION_SUBGRAPH::GetDriverPriority( resolvedDriver );
1552 }
1553 }
1554 else if( CONNECTION_GRAPH* connGraph = schematic.ConnectionGraph() )
1555 {
1556 CONNECTION_SUBGRAPH* sg = connGraph->GetSubgraphForItem( aPin );
1557
1558 if( sg && sg->GetDriver() )
1559 {
1560 resolvedDriver = const_cast<SCH_ITEM*>( sg->GetDriver() );
1561 driverSheet = sg->GetSheet();
1562 resolvedPriority = sg->GetDriverPriority();
1563 }
1564 }
1565
1566 if( resolvedDriver && resolvedPriority >= CONNECTION_SUBGRAPH::PRIORITY::LOCAL_LABEL )
1567 {
1569 msg.Printf( _( "Change %s pin %s net label from '%s' to '%s'." ), DescribeRef( aRef ),
1570 EscapeHTML( aPin->GetShownNumber() ), EscapeHTML( aOldName ), EscapeHTML( aNewName ) );
1571
1572 if( !m_dryRun )
1573 {
1574 if( resolvedDriver->Type() == SCH_PIN_T )
1575 {
1576 SCH_PIN* powerPin = static_cast<SCH_PIN*>( resolvedDriver );
1577 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( powerPin->GetParentSymbol() );
1578
1579 aCommit->Modify( symbol, driverSheet.LastScreen() );
1580 symbol->GetField( FIELD_T::VALUE )->SetText( aNewName );
1581 }
1582 else
1583 {
1584 aCommit->Modify( resolvedDriver, driverSheet.LastScreen() );
1585 static_cast<SCH_LABEL_BASE*>( resolvedDriver )->SetText( aNewName );
1586 }
1587 }
1588
1589 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1590 break;
1591 }
1592 }
1593
1595 msg.Printf( _( "Add label '%s' to %s pin %s net." ),
1596 EscapeHTML( aNewName ),
1597 DescribeRef( aRef ),
1598 EscapeHTML( aPin->GetShownNumber() ) );
1599
1600 if( !m_dryRun )
1601 {
1602 SCHEMATIC_SETTINGS& settings = m_frame->Schematic().Settings();
1603 SCH_LABEL* label = new SCH_LABEL( driver->GetPosition(), aNewName );
1604 label->SetParent( &m_frame->Schematic() );
1605 label->SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
1606 label->SetSpinStyle( orientLabel( static_cast<SCH_PIN*>( driver ) ) );
1607 label->SetFlags( IS_NEW );
1608
1609 SCH_SCREEN* screen = aSheet.LastScreen();
1610 aCommit->Add( label, screen );
1611 }
1612
1613 m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
1614 break;
1615
1616 default:
1617 break;
1618 }
1619}
const char * name
bool CollectBackannotatePcbNets(const SCH_SYMBOL &aSymbol, const SCH_SHEET_PATH &aSheetPath, const wxString &aVariantName, const std::map< wxString, wxString > &aPcbNetsByPad, std::map< wxString, wxString > &aPcbNetsByLogicalPin)
Append this unit's PCB nets to a map keyed by logical pin number, resolving pin-to-pad mappings.
static std::vector< wxString > netsInUnitOrder(const std::vector< wxString > &aPins, const std::map< wxString, wxString > &aNetByPin)
void addConnections(SCH_ITEM *aItem, const SCH_SHEET_PATH &aSheetPath, std::set< SCH_ITEM * > &connectedItems)
static SPIN_STYLE orientLabel(SCH_PIN *aPin)
BACKANNOTATE_UNIT_SWAP_PLAN PlanBackannotateUnitSwaps(const std::vector< BACKANNOTATE_UNIT_SWAP_CANDIDATE > &aCandidates, const std::map< wxString, wxString > &aPcbPinMap)
Compute a pure unit-swap plan from schematic-side unit definitions and PCB nets keyed by logical pin ...
bool CollectBackannotatePcbNets(const SCH_SYMBOL &aSymbol, const SCH_SHEET_PATH &aSheetPath, const wxString &aVariantName, const std::map< wxString, wxString > &aPcbNetsByPad, std::map< wxString, wxString > &aPcbNetsByLogicalPin)
Append this unit's PCB nets to a map keyed by logical pin number, resolving pin-to-pad mappings.
BACKANNOTATE_UNIT_SWAP_PLAN PlanBackannotateUnitSwaps(const std::vector< BACKANNOTATE_UNIT_SWAP_CANDIDATE > &aCandidates, const std::map< wxString, wxString > &aPcbPinMap)
Compute a pure unit-swap plan from schematic-side unit definitions and PCB nets keyed by logical pin ...
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
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_pcbFootprints.
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
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.
void processNetNameChange(SCH_COMMIT *aCommit, const wxString &aRef, SCH_PIN *aPin, const SCH_SHEET_PATH &aSheet, const wxString &aOldName, const wxString &aNewName)
bool m_matchByReference
void getChangeList()
Create changelist.
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:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
Calculate the connectivity of a schematic and generate netlists.
A subgraph is a set of items that are electrically connected on a single sheet.
const SCH_ITEM * GetDriver() const
static PRIORITY GetDriverPriority(SCH_ITEM *aDriver)
Return the priority (higher is more important) of a candidate driver.
const SCH_SHEET_PATH & GetSheet() const
Implement a lexical analyzer for the SPECCTRA DSN file format.
Definition dsnlexer.h:75
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
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:160
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:153
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:167
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
bool HasTextVars() const
Indicates the ShownText has text var references which need to be processed.
Definition eda_text.h:139
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
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:114
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:207
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:130
These are loaded from Eeschema settings but then overwritten by the project settings.
Holds all the data relating to one schematic.
Definition schematic.h:149
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
SCH_CONNECTIVITY::FACADE & Connectivity() const
Definition schematic.h:316
CONNECTION_GRAPH * ConnectionGraph() const
Definition schematic.h:319
Commit for the schematic and symbol editors.
Definition sch_commit.h:54
void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
std::optional< NET_VIEW > GetSubgraphForItem(const KIID &aItem, const KIID_PATH &aPath) const
std::optional< ITEM_VIEW > Connection(const KIID &aItem, const KIID_PATH &aPath) const
Schematic editor (Eeschema) main window.
wxString GetUntranslatedName() const
Get the untranslated field name for storage, variable look-up, etc.
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString, 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:170
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:350
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:302
const std::vector< SCH_ITEM * > & ConnectedItems(const SCH_SHEET_PATH &aPath) const
Retrieve the set of items connected to this item on the given sheet.
Definition sch_item.cpp:766
int GetUnit() const
Definition sch_item.h:243
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Tool responsible for drawing/placing items (symbols, wires, buses, labels, etc.)
SCH_PIN * GetLibPin() const
Definition sch_pin.h:109
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:386
const wxString & GetShownNumber() const
Definition sch_pin.cpp:708
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
int GetUnit() const
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
std::optional< SCH_SHEET_PATH > GetSheetPathByKIIDPath(const KIID_PATH &aPath, bool aIncludeLastSheet=true) const
Finds a SCH_SHEET_PATH that matches the provided KIID_PATH.
void GetMultiUnitSymbols(SCH_MULTI_UNIT_REFERENCE_MAP &aRefList, SYMBOL_FILTER aSymbolFilter) 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, SYMBOL_FILTER aSymbolFilter, 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()
const KIID_PATH & PathRef() const
Borrow the cached path until this sheet path is modified or destroyed.
Schematic symbol object.
Definition sch_symbol.h:73
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
virtual void SetDNP(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
void RemoveField(const wxString &aFieldName)
Remove a user field from the symbol.
void SetExcludedFromSim(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear the exclude from simulation flag.
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
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:895
void SetExcludedFromPosFiles(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
void SetValueFieldText(const wxString &aValue, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString)
bool GetExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
bool GetExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
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:182
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)
A null aItem is ignored; the selection never holds null members.
Definition selection.cpp:38
Spin style for labels of all kinds on schematics.
Definition sch_label.h:38
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()
Master controller class:
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:67
wxString DescribeRef(const wxString &aRef)
Returns a user-visible HTML string describing a footprint reference designator.
Definition common.cpp:508
@ INTERNAL
Definition common.h:94
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
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:38
static wxString From_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition idf_helpers.h:44
@ MAIL_PCB_UPDATE_LINKS
Definition mail_type.h:48
@ MAIL_PCB_GET_NETLIST
Definition mail_type.h:47
static std::vector< PENDING_PROPERTY > plan(const EDA_ITEM &aSource, const EDA_ITEM &aTarget, const std::set< wxString > &aEnabledKeys)
Target properties to write, paired with values read off the source.
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
void Scan(PTREE *aTree, DSNLEXER *aLexer)
Fill an empty PTREE with information from a KiCad s-expression stream.
Definition ptree.cpp:80
boost::property_tree::ptree PTREE
Definition ptree.h:48
@ 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.
@ SYMBOL_FILTER_NON_POWER
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:93
wxString EscapeHTML(const wxString &aString)
Return a new wxString escaped for embedding in HTML.
std::vector< wxString > ExpandStackedPinNotation(const wxString &aPinName, bool *aValid)
Expand stacked pin notation like [1,2,3], [1-4], [A1-A4], or [AA1-AA3,AB4,CD12-CD14] into individual ...
void AccumulateDescriptions(wxString &aDesc, const T &aItemCollection)
Build a comma-separated list from a collection of wxStrings.
std::vector< wxString > m_unitPinNumbers
std::map< wxString, wxString > m_schNetsByPin
Container for Pcbnew footprint data.
std::map< wxString, wxString > m_pinMap
std::map< wxString, wxString > m_fieldsMap
@ SYM_ORIENT_270
Definition symbol.h:39
@ SYM_MIRROR_Y
Definition symbol.h:41
@ SYM_ORIENT_180
Definition symbol.h:38
@ SYM_MIRROR_X
Definition symbol.h:40
@ SYM_ORIENT_90
Definition symbol.h:37
@ SYM_ORIENT_0
Definition symbol.h:36
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ 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".
@ UNTRANSLATED
std::string path
KIBIS_PIN * partner
KIBIS_PIN * pin
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
#define kv
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_SHEET_PIN_T
Definition typeinfo.h:170
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_PIN_T
Definition typeinfo.h:149
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
Definition of file extensions used in Kicad.