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