KiCad PCB EDA Suite
Loading...
Searching...
No Matches
netlist_exporter_xml.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) 1992-2013 jp.charras at wanadoo.fr
5 * Copyright (C) 2013-2017 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
23
24#include <algorithm>
25#include <build_version.h>
26#include <gal/color4d.h>
27#include <common.h> // for ExpandTextVars
28#include <sch_base_frame.h>
29#include <sch_group.h>
30#include <sch_sheet.h>
31#include <string_utils.h>
32#include <connection_graph.h>
33#include <pgm_base.h>
34#include <core/kicad_algo.h>
35#include <wx/wfstream.h>
36#include <xnode.h> // also nests: <wx/xml/xml.h>
37#include <json_common.h>
38#include <project_sch.h>
41#include <sch_rule_area.h>
42#include <trace_helpers.h>
43
44#include <map>
45#include <optional>
46#include <set>
48#include <sch_sheet_path.h>
49
50static bool sortPinsByNumber( SCH_PIN* aPin1, SCH_PIN* aPin2 );
51
52bool NETLIST_EXPORTER_XML::writeNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
53 REPORTER& aReporter )
54{
55 // output the XML format netlist.
56
57 // declare the stream ourselves to use the buffered FILE api
58 // instead of letting wx use the syscall variant
59 wxFFileOutputStream stream( aOutFileName );
60
61 if( !stream.IsOk() )
62 return false;
63
64 wxXmlDocument xdoc;
65 xdoc.SetRoot( makeRoot( GNL_ALL | aNetlistOptions ) );
66
67 return xdoc.Save( stream, 2 /* indent bug, today was ignored by wxXml lib */ );
68}
69
70
72{
73 XNODE* xroot = node( wxT( "export" ) );
74
75 xroot->AddAttribute( wxT( "version" ), wxT( "E" ) );
76
77 if( aCtl & GNL_HEADER )
78 {
79 // add the "design" header
80 xroot->AddChild( makeDesignHeader() );
81 }
82
83 if( aCtl & GNL_SYMBOLS )
84 {
85 xroot->AddChild( makeSymbols( aCtl ) );
86
87 if( aCtl & GNL_OPT_KICAD )
88 {
89 xroot->AddChild( makeGroups() );
90 xroot->AddChild( makeVariants() );
91 }
92 }
93
94 if( aCtl & GNL_PARTS )
95 xroot->AddChild( makeLibParts() );
96
97 if( aCtl & GNL_LIBRARIES )
98 {
99 // must follow makeGenericLibParts()
100 xroot->AddChild( makeLibraries() );
101 }
102
103 if( aCtl & GNL_NETS )
104 {
105 xroot->AddChild( makeListOfNets( aCtl ) );
106
107 // Net chains are a KiCad-internal extension that is not part of the public XML
108 // netlist schema (version "E"). Emit them only for the KiCad-internal consumer
109 // (eeschema -> pcbnew via NETLIST_EXPORTER_KICAD) so that generic XML, KiCost and
110 // other schema-validating tools do not see an unexpected element.
111 if( aCtl & GNL_OPT_KICAD )
112 {
113 if( XNODE* xchains = makeNetChains() )
114 xroot->AddChild( xchains );
115 }
116 }
117
118 return xroot;
119}
120
121
123
124
126 const SCH_SHEET_LIST& aSheetList )
127{
128 wxString value;
129 wxString footprint;
130 wxString datasheet;
131 wxString description;
132 wxString candidate;
133 nlohmann::ordered_map<wxString, wxString> fields;
134
135 if( aSymbol->GetUnitCount() > 1 )
136 {
137 // Sadly, each unit of a symbol can have its own unique fields. This
138 // block finds the unit with the lowest number having a non blank field
139 // value and records it. Therefore user is best off setting fields
140 // into only the first unit. But this scavenger algorithm will find
141 // any non blank fields in all units and use the first non-blank field
142 // for each unique field name.
143
144 wxString ref = aSymbol->GetRef( &aSheet );
145
146 // ranked by unit then UUID so the pick does not depend on visiting order
147 using SOURCE = std::pair<int, KIID>;
148
149 std::optional<SOURCE> valueSrc;
150 std::optional<SOURCE> footprintSrc;
151 std::optional<SOURCE> datasheetSrc;
152 std::optional<SOURCE> descriptionSrc;
153
154 // a blank field ranks after any text so it never hides a higher unit's value
155 std::map<wxString, std::pair<bool, SOURCE>> fieldSrcs;
156
157 auto earlier =
158 []( const SOURCE& aNew, const std::optional<SOURCE>& aOld )
159 {
160 return !aOld || aNew < *aOld;
161 };
162
163 for( const SCH_SHEET_PATH& sheet : aSheetList )
164 {
165 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
166 {
167 SCH_SYMBOL* symbol2 = static_cast<SCH_SYMBOL*>( item );
168
169 wxString ref2 = symbol2->GetRef( &sheet );
170
171 if( ref2.CmpNoCase( ref ) != 0 )
172 continue;
173
174 const SOURCE src{ symbol2->GetUnitSelection( &sheet ), symbol2->m_Uuid };
175
176 // The lowest unit number wins. User should only set fields in any one unit.
177
178 // Value
179 candidate = symbol2->GetValue( &sheet, m_resolveTextVars, m_exportVariant );
180
181 if( !candidate.IsEmpty() && earlier( src, valueSrc ) )
182 {
183 value = candidate;
184 valueSrc = src;
185 }
186
187 // Footprint
188 candidate = symbol2->GetFootprintFieldText( &sheet, m_resolveTextVars, m_exportVariant );
189
190 if( !candidate.IsEmpty() && earlier( src, footprintSrc ) )
191 {
192 footprint = candidate;
193 footprintSrc = src;
194 }
195
196 // Datasheet
197 candidate = symbol2->GetField( FIELD_T::DATASHEET )->GetShownText( &sheet, m_resolveTextVars,
199
200 if( !candidate.IsEmpty() && earlier( src, datasheetSrc ) )
201 {
202 datasheet = candidate;
203 datasheetSrc = src;
204 }
205
206 // Description
207 candidate = symbol2->GetField( FIELD_T::DESCRIPTION )->GetShownText( &sheet, m_resolveTextVars,
209
210 if( !candidate.IsEmpty() && earlier( src, descriptionSrc ) )
211 {
212 description = candidate;
213 descriptionSrc = src;
214 }
215
216 // All non-mandatory fields
217 for( SCH_FIELD& field : symbol2->GetFields() )
218 {
219 if( field.IsMandatory() || field.IsPrivate() )
220 continue;
221
222 candidate = field.GetShownText( &sheet, m_resolveTextVars, m_exportVariant );
223
224 const std::pair<bool, SOURCE> rank{ candidate.IsEmpty(), src };
225 auto known = fieldSrcs.find( field.GetName() );
226
227 if( known == fieldSrcs.end() || rank < known->second )
228 {
229 fields[field.GetName()] = candidate;
230 fieldSrcs.insert_or_assign( field.GetName(), rank );
231 }
232 }
233 }
234 }
235 }
236 else
237 {
238 value = aSymbol->GetValue( &aSheet, m_resolveTextVars, m_exportVariant );
239 footprint = aSymbol->GetFootprintFieldText( &aSheet, m_resolveTextVars, m_exportVariant );
240
241 SCH_FIELD* datasheetField = aSymbol->GetField( FIELD_T::DATASHEET );
242 SCH_FIELD* descriptionField = aSymbol->GetField( FIELD_T::DESCRIPTION );
243
244 datasheet = datasheetField->GetShownText( &aSheet, m_resolveTextVars, m_exportVariant );
245 description = descriptionField->GetShownText( &aSheet, m_resolveTextVars, m_exportVariant );
246
247 for( SCH_FIELD& field : aSymbol->GetFields() )
248 {
249 if( field.IsMandatory() || field.IsPrivate() )
250 continue;
251
252 fields[field.GetName()] = field.GetShownText( &aSheet, m_resolveTextVars, m_exportVariant );
253 }
254 }
255
258 fields[GetDefaultFieldName( FIELD_T::DESCRIPTION, UNTRANSLATED )] = description;
259
260 // Do not output field values blank in netlist:
261 if( value.size() )
262 aNode->AddChild( node( wxT( "value" ), UnescapeString( value ) ) );
263 else // value field always written in netlist
264 aNode->AddChild( node( wxT( "value" ), wxT( "~" ) ) );
265
266 if( footprint.size() )
267 aNode->AddChild( node( wxT( "footprint" ), UnescapeString( footprint ) ) );
268
269 if( datasheet.size() )
270 aNode->AddChild( node( wxT( "datasheet" ), UnescapeString( datasheet ) ) );
271
272 if( description.size() )
273 aNode->AddChild( node( wxT( "description" ), UnescapeString( description ) ) );
274
275 XNODE* xfields;
276 aNode->AddChild( xfields = node( wxT( "fields" ) ) );
277
278 for( const auto& [ fieldName, fieldValue ] : fields )
279 {
280 XNODE* xfield = node( wxT( "field" ), UnescapeString( fieldValue ) );
281 xfield->AddAttribute( wxT( "name" ), UnescapeString( fieldName ) );
282 xfields->AddChild( xfield );
283 }
284}
285
286
288{
289 XNODE* xcomps = node( wxT( "components" ) );
290
292 m_libParts.clear();
294
295 SCH_SHEET_PATH currentSheet = m_schematic->CurrentSheet();
296 SCH_SHEET_LIST sheetList = m_exportSheets;
297
298 // Output is xml, so there is no reason to remove spaces from the field values.
299 // And XML element names need not be translated to various languages.
300
301 for( const SCH_SHEET_PATH& sheet : sheetList )
302 {
303 // Change schematic CurrentSheet in each iteration to allow hierarchical
304 // resolution of text variables in sheet fields.
305 m_schematic->SetCurrentSheet( sheet );
306
307 auto cmp =
308 [&sheet]( SCH_SYMBOL* a, SCH_SYMBOL* b )
309 {
310 return ( StrNumCmp( a->GetRef( &sheet, false ),
311 b->GetRef( &sheet, false ), true ) < 0 );
312 };
313
314 std::set<SCH_SYMBOL*, decltype( cmp )> ordered_symbols( cmp );
315 std::multiset<SCH_SYMBOL*, decltype( cmp )> extra_units( cmp );
316
317 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
318 {
319 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
320 auto test = ordered_symbols.insert( symbol );
321
322 if( !test.second )
323 {
324 if( ( *( test.first ) )->m_Uuid > symbol->m_Uuid )
325 {
326 extra_units.insert( *( test.first ) );
327 ordered_symbols.erase( test.first );
328 ordered_symbols.insert( symbol );
329 }
330 else
331 {
332 extra_units.insert( symbol );
333 }
334 }
335 }
336
337 for( EDA_ITEM* item : ordered_symbols )
338 {
339 SCH_SYMBOL* symbol = findNextSymbol( item, sheet );
340 bool forBOM = aCtl & GNL_OPT_BOM;
341 bool forBoard = aCtl & GNL_OPT_KICAD;
342
343 if( !symbol )
344 continue;
345
346 // Do not use exportVariant for determininig whether or not to output a symbol. It
347 // will be blank when exporting to PCBNew.
348
349 if( forBOM && ( sheet.GetExcludedFromBOM( m_schematic->GetCurrentVariant() )
350 || symbol->ResolveExcludedFromBOM( &sheet, m_schematic->GetCurrentVariant() ) ) )
351 {
352 continue;
353 }
354
355 if( forBoard && ( sheet.GetExcludedFromBoard( m_schematic->GetCurrentVariant() )
356 || symbol->ResolveExcludedFromBoard( &sheet, m_schematic->GetCurrentVariant() ) ) )
357 {
358 continue;
359 }
360
361 // Output the symbol's elements in order of expected access frequency. This may
362 // not always look best, but it will allow faster execution under XSL processing
363 // systems which do sequential searching within an element.
364
365 XNODE* xcomp; // current symbol being constructed
366 xcomps->AddChild( xcomp = node( wxT( "comp" ) ) );
367
368 xcomp->AddAttribute( wxT( "ref" ), symbol->GetRef( &sheet ) );
369 addSymbolFields( xcomp, symbol, sheet, sheetList );
370
371 XNODE* xlibsource;
372 xcomp->AddChild( xlibsource = node( wxT( "libsource" ) ) );
373
374 // "logical" library name, which is in anticipation of a better search algorithm
375 // for parts based on "logical_lib.part" and where logical_lib is merely the library
376 // name minus path and extension.
377 wxString libName;
378 wxString partName;
379
380 if( symbol->UseLibIdLookup() )
381 {
382 libName = symbol->GetLibId().GetUniStringLibNickname();
383 partName = symbol->GetLibId().GetUniStringLibItemName();
384 }
385 else
386 {
387 partName = symbol->GetSchSymbolLibraryName();
388 }
389
390 xlibsource->AddAttribute( wxT( "lib" ), libName );
391
392 // We only want the symbol name, not the full LIB_ID.
393 xlibsource->AddAttribute( wxT( "part" ), partName );
394
395 xlibsource->AddAttribute( wxT( "description" ), symbol->GetShownDescription( m_resolveTextVars ) );
396
397 /* Add the symbol properties. */
398 XNODE* xproperty;
399
400 std::vector<SCH_FIELD>& fields = symbol->GetFields();
401
402 for( SCH_FIELD& field : fields )
403 {
404 if( field.IsMandatory() || field.IsPrivate() )
405 continue;
406
407 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
408 xproperty->AddAttribute( wxT( "name" ), field.GetUntranslatedName() );
409
410 xproperty->AddAttribute( wxT( "value" ), field.GetShownText( &sheet, m_resolveTextVars,
411 m_exportVariant ) );
412 }
413
414 for( const SCH_FIELD& sheetField : sheet.Last()->GetFields() )
415 {
416 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
417 xproperty->AddAttribute( wxT( "name" ), sheetField.GetUntranslatedName() );
418
419 xproperty->AddAttribute( wxT( "value" ), sheetField.GetShownText( &sheet, m_resolveTextVars,
420 m_exportVariant ) );
421 }
422
423 const bool baseExcludedFromBOM = symbol->ResolveExcludedFromBOM( &sheet ) || sheet.GetExcludedFromBOM();
424 const bool baseExcludedFromSim = symbol->ResolveExcludedFromSim( &sheet ) || sheet.GetExcludedFromSim();
425
426 if( symbol->ResolveExcludedFromBOM( &sheet, m_exportVariant )
427 || sheet.GetExcludedFromBOM( m_exportVariant ) )
428 {
429 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
430 xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_bom" ) );
431 }
432
433 if( symbol->ResolveExcludedFromSim( &sheet, m_exportVariant )
434 || sheet.GetExcludedFromSim( m_exportVariant ) )
435 {
436 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
437 xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_sim" ) );
438 }
439
440 if( symbol->ResolveExcludedFromBoard( &sheet, m_exportVariant )
441 || sheet.GetExcludedFromBoard( m_exportVariant ) )
442 {
443 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
444 xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_board" ) );
445 }
446
447 const bool baseExcludedFromPosFiles = symbol->ResolveExcludedFromPosFiles( &sheet );
448
449 if( symbol->ResolveExcludedFromPosFiles( &sheet, m_exportVariant ) )
450 {
451 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
452 xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_pos_files" ) );
453 }
454
455 const bool baseDnp = symbol->ResolveDNP( &sheet ) || sheet.GetDNP();
456
457 if( symbol->ResolveDNP( &sheet, m_exportVariant ) || sheet.GetDNP( m_exportVariant ) )
458 {
459 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
460 xproperty->AddAttribute( wxT( "name" ), wxT( "dnp" ) );
461 }
462
463 // Iterate all variants in the schematic, not just those in the symbol instance,
464 // because a sheet can have variant-specific attributes even if the symbol does not.
465 const std::set<wxString>& variantNames = m_schematic->GetVariantNames();
466
467 // Differences against the base design, which only the board netlist still reports.
468 if( ( aCtl & GNL_OPT_KICAD ) && !variantNames.empty() )
469 {
470 XNODE* xvariants = nullptr;
471
472 for( const auto& variantName : variantNames )
473 {
474 XNODE* xvariant = nullptr;
475
476 auto addToVariant =
477 [this, &xvariant, &variantName]( XNODE* child ) -> void
478 {
479 if( !child )
480 return;
481
482 if( !xvariant )
483 {
484 xvariant = node( wxT( "variant" ) );
485 xvariant->AddAttribute( wxT( "name" ), variantName );
486 }
487 xvariant->AddChild( child );
488 };
489
490 auto addBinaryProp =
491 [this, &addToVariant]( wxString const& name, bool base, bool effective ) -> void
492 {
493 if( base == effective )
494 return;
495
496 XNODE* xvarprop = node( wxT( "property" ) );
497 xvarprop->AddAttribute( wxT( "name" ), name );
498 xvarprop->AddAttribute( wxT( "value" ), effective ? wxT( "1" ) : wxT( "0" ) );
499 addToVariant( xvarprop );
500 };
501
502 bool effectiveDnp = symbol->ResolveDNP( &sheet, variantName ) || sheet.GetDNP( variantName );
503 addBinaryProp( wxT( "dnp" ), baseDnp, effectiveDnp );
504
505 bool effectiveExcludedFromBOM = symbol->ResolveExcludedFromBOM( &sheet, variantName )
506 || sheet.GetExcludedFromBOM( variantName );
507 addBinaryProp( wxT( "exclude_from_bom" ), baseExcludedFromBOM, effectiveExcludedFromBOM );
508
509 bool effectiveExcludedFromSim = symbol->ResolveExcludedFromSim( &sheet, variantName )
510 || sheet.GetExcludedFromSim( variantName );
511 addBinaryProp( wxT( "exclude_from_sim" ), baseExcludedFromSim, effectiveExcludedFromSim );
512
513 bool effectiveExcludedFromPosFiles = symbol->ResolveExcludedFromPosFiles( &sheet, variantName );
514 addBinaryProp( wxT( "exclude_from_pos_files" ), baseExcludedFromPosFiles,
515 effectiveExcludedFromPosFiles );
516
517 SCH_SYMBOL_INSTANCE instance;
518 const SCH_SYMBOL_VARIANT* variant = nullptr;
519
520 if( symbol->GetInstance( instance, sheet.Path() ) && instance.m_Variants.contains( variantName ) )
521 variant = &instance.m_Variants.at( variantName );
522
523 if( variant && variant->m_SymbolOverride )
524 {
525 XNODE* xprop = node( wxT( "property" ) );
526 xprop->AddAttribute( wxT( "name" ), wxT( "symbol_override" ) );
527 xprop->AddAttribute( wxT( "value" ), variant->m_SymbolOverride->Format() );
528 addToVariant( xprop );
529 }
530
531 if( variant && ( !variant->m_Fields.empty() || variant->m_SymbolOverride ) )
532 {
533 XNODE* xfields = nullptr;
534
535 for( const auto& [fieldName, fieldValue] : variant->m_Fields )
536 {
537 const wxString baseValue = symbol->GetFieldText( fieldName, &sheet, wxEmptyString );
538
539 if( fieldValue == baseValue )
540 continue;
541
542 if( !xfields )
543 xfields = node( wxT( "fields" ) );
544
545 wxString resolvedValue = fieldValue;
546
548 resolvedValue = symbol->ResolveText( fieldValue, &sheet );
549
550 XNODE* xfield = node( wxT( "field" ), UnescapeString( resolvedValue ) );
551 xfield->AddAttribute( wxT( "name" ), UnescapeString( fieldName ) );
552 xfields->AddChild( xfield );
553 }
554
555 // Emit fields resolved from the alternate library symbol where the
556 // value differs from the base symbol. Explicit overrides in
557 // variant->m_Fields were already handled above.
558 if( variant->m_SymbolOverride )
559 {
560 for( const SCH_FIELD& baseField : symbol->GetFields() )
561 {
562 const wxString& fieldName = baseField.GetName();
563
564 if( variant->m_Fields.contains( fieldName ) )
565 continue;
566
567 const wxString baseValue = symbol->GetFieldText( fieldName, &sheet, wxEmptyString );
568 const wxString variantValue = symbol->GetFieldText( fieldName, &sheet, variantName );
569
570 if( variantValue == baseValue )
571 continue;
572
573 if( !xfields )
574 xfields = node( wxT( "fields" ) );
575
576 wxString resolvedValue = variantValue;
577
579 resolvedValue = symbol->ResolveText( variantValue, &sheet );
580
581 XNODE* xfield = node( wxT( "field" ), UnescapeString( resolvedValue ) );
582 xfield->AddAttribute( wxT( "name" ), UnescapeString( baseField.GetUntranslatedName() ) );
583 xfields->AddChild( xfield );
584 }
585
586 // Emit fields that exist only on the alternate library symbol so the
587 // netlist reflects the full atomic part, not just the base field set.
588 if( LIB_SYMBOL* altSymbol = symbol->GetVariantLibSymbol( variantName, sheet ) )
589 {
590 std::vector<SCH_FIELD*> altFields;
591 altSymbol->GetFields( altFields );
592
593 for( const SCH_FIELD* altField : altFields )
594 {
595 const wxString& fieldName = altField->GetName();
596
597 if( symbol->GetField( fieldName )
598 || variant->m_Fields.contains( fieldName )
599 || altField->GetText().IsEmpty() )
600 {
601 continue;
602 }
603
604 if( !xfields )
605 xfields = node( wxT( "fields" ) );
606
607 wxString resolvedValue = altField->GetText();
608
610 resolvedValue = symbol->ResolveText( resolvedValue, &sheet );
611
612 XNODE* xfield = node( wxT( "field" ), UnescapeString( resolvedValue ) );
613 xfield->AddAttribute( wxT( "name" ), UnescapeString( fieldName ) );
614 xfields->AddChild( xfield );
615 }
616 }
617 }
618
619 addToVariant( xfields );
620 }
621
622 if( xvariant )
623 {
624 if( !xvariants )
625 xvariants = node( wxT( "variants" ) );
626
627 xvariants->AddChild( xvariant );
628 }
629 }
630
631 if( xvariants )
632 xcomp->AddChild( xvariants );
633 }
634
635 if( const std::unique_ptr<LIB_SYMBOL>& part = symbol->GetLibSymbolRef() )
636 {
637 if( part->GetKeyWords().size() )
638 {
639 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
640 xproperty->AddAttribute( wxT( "name" ), wxT( "ki_keywords" ) );
641 xproperty->AddAttribute( wxT( "value" ), part->GetKeyWords() );
642 }
643
644 if( !part->GetFPFilters().IsEmpty() )
645 {
646 wxString filters;
647
648 for( const wxString& filter : part->GetFPFilters() )
649 filters += ' ' + filter;
650
651 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
652 xproperty->AddAttribute( wxT( "name" ), wxT( "ki_fp_filters" ) );
653 xproperty->AddAttribute( wxT( "value" ), filters.Trim( false ) );
654 }
655
656 if( part->GetDuplicatePinNumbersAreJumpers() )
657 xcomp->AddChild( node( wxT( "duplicate_pin_numbers_are_jumpers" ), wxT( "1" ) ) );
658
659 const JUMPER_GROUP_SET& jumperGroups = part->JumperPinGroups();
660
661 if( !jumperGroups.IsEmpty() )
662 {
663 XNODE* groupNode;
664 xcomp->AddChild( xproperty = node( wxT( "jumper_pin_groups" ) ) );
665
666 for( const JUMPER_GROUP& group : jumperGroups.GetAll() )
667 {
668 xproperty->AddChild( groupNode = node( wxT( "group" ) ) );
669
670 for( const wxString& pinName : group.GetNames() )
671 groupNode->AddChild( node( wxT( "pin" ), pinName ) );
672 }
673 }
674 }
675
676 XNODE* xsheetpath;
677 xcomp->AddChild( xsheetpath = node( wxT( "sheetpath" ) ) );
678
679 xsheetpath->AddAttribute( wxT( "names" ), sheet.PathHumanReadable() );
680 xsheetpath->AddAttribute( wxT( "tstamps" ), sheet.PathAsString() );
681
682 // Node for component class
683 std::vector<wxString> compClassNames = getComponentClassNamesForAllSymbolUnits( symbol, sheet, sheetList );
684
685 if( compClassNames.size() > 0 )
686 {
687 XNODE* xcompclasslist;
688 xcomp->AddChild( xcompclasslist = node( wxT( "component_classes" ) ) );
689
690 for( const wxString& compClass : compClassNames )
691 xcompclasslist->AddChild( node( wxT( "class" ), UnescapeString( compClass ) ) );
692 }
693
694 XNODE* xunits; // Node for extra units
695 xcomp->AddChild( xunits = node( wxT( "tstamps" ) ) );
696
697 auto range = extra_units.equal_range( symbol );
698 wxString uuid;
699
700 // Output a series of children with all UUIDs associated with the REFDES
701 for( auto it = range.first; it != range.second; ++it )
702 {
703 uuid = ( *it )->m_Uuid.AsString();
704
705 // Add a space between UUIDs, if not in KICAD mode (i.e.
706 // using wxXmlDocument::Save()). KICAD MODE has its own XNODE::Format function.
707 if( !( aCtl & GNL_OPT_KICAD ) ) // i.e. for .xml format
708 uuid += ' ';
709
710 xunits->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, uuid ) );
711 }
712
713 // Output the primary UUID
714 uuid = symbol->m_Uuid.AsString();
715 xunits->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, uuid ) );
716
717 // Emit unit information (per-unit name and pins) after tstamps
718 XNODE* xunitInfo;
719 xcomp->AddChild( xunitInfo = node( wxT( "units" ) ) );
720
721 const std::unique_ptr<LIB_SYMBOL>& libSym = symbol->GetLibSymbolRef();
722
723 if( libSym )
724 {
725 // A multi-unit symbol can resolve to a different lib symbol per placed unit
726 // after unit-specific edits. For instane, if you have units A B C in a multi-unit
727 // symbol, and you edit only unit B, unit B will point to new, modified lib symbol
728 // but A and C will point to the original lib symbol.
729 //
730 // Export the unit metadata from the actual unit instances's lib symbols so the PCB
731 // footprint metadata matches during backannotation, which always works per unit.
732 //
733 // However, the user isn't required to place all units, so keep a fallback default unit info.
734 const std::vector<LIB_SYMBOL::UNIT_PIN_INFO>& defaultUnitInfo = libSym->GetUnitPinInfo();
735 std::map<int, SCH_SYMBOL*> symbolByUnit;
736
737 auto addUnitSymbol =
738 [&]( SCH_SYMBOL* aUnitSymbol )
739 {
740 if( !aUnitSymbol )
741 return;
742
743 int unit = aUnitSymbol->GetUnitSelection( &sheet );
744
745 if( unit > 0 )
746 symbolByUnit.try_emplace( unit, aUnitSymbol );
747 };
748
749 addUnitSymbol( symbol );
750
751 auto extraUnitRange = extra_units.equal_range( symbol );
752
753 // Collect the other placed units that share this reference so each unit number
754 // can be resolved back to the specific SCH_SYMBOL instance on the sheet.
755 for( auto it = extraUnitRange.first; it != extraUnitRange.second; ++it )
756 addUnitSymbol( *it );
757
758 // Emit every unit slot from the default library symbol, but override that slot's
759 // metadata with the placed unit's resolved library symbol when one exists.
760 for( size_t unitIdx = 0; unitIdx < defaultUnitInfo.size(); ++unitIdx )
761 {
762 LIB_SYMBOL::UNIT_PIN_INFO unitInfo = defaultUnitInfo[unitIdx];
763 auto symbolIt = symbolByUnit.find( unitIdx + 1 );
764
765 if( symbolIt != symbolByUnit.end() )
766 {
767 const std::unique_ptr<LIB_SYMBOL>& unitLibSym = symbolIt->second->GetLibSymbolRef();
768
769 if( unitLibSym )
770 {
771 const std::vector<LIB_SYMBOL::UNIT_PIN_INFO>& unitSpecificInfo =
772 unitLibSym->GetUnitPinInfo();
773
774 if( unitIdx < unitSpecificInfo.size() )
775 unitInfo = unitSpecificInfo[unitIdx];
776 }
777 }
778
779 XNODE* xunit;
780 xunitInfo->AddChild( xunit = node( wxT( "unit" ) ) );
781 xunit->AddAttribute( wxT( "name" ), unitInfo.m_unitName );
782
783 XNODE* xpins;
784 xunit->AddChild( xpins = node( wxT( "pins" ) ) );
785
786 for( const wxString& number : unitInfo.m_pinNumbers )
787 {
788 XNODE* xpin;
789 xpins->AddChild( xpin = node( wxT( "pin" ) ) );
790 xpin->AddAttribute( wxT( "num" ), number );
791 }
792 }
793 }
794 }
795 }
796
797 m_schematic->SetCurrentSheet( currentSheet );
798
799 return xcomps;
800}
801
802
804{
805 XNODE* xcomps = node( wxT( "groups" ) );
806
808 // Do not clear m_libParts here: it is populated in makeSymbols() and used later by
809 // makeLibParts() to emit the libparts section for CvPcb and other consumers.
810
811 SCH_SHEET_PATH currentSheet = m_schematic->CurrentSheet();
812 SCH_SHEET_LIST sheetList = m_exportSheets;
813 std::map<SCH_SCREEN*, int> screenVisits;
814
815 for( const SCH_SHEET_PATH& sheet : sheetList )
816 screenVisits[sheet.LastScreen()]++;
817
818 for( const SCH_SHEET_PATH& sheet : sheetList )
819 {
820 // Change schematic CurrentSheet in each iteration to allow hierarchical
821 // resolution of text variables in sheet fields.
822 m_schematic->SetCurrentSheet( sheet );
823
824 wxString instancePrefix = sheet.PathAsString();
825
826 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GROUP_T ) )
827 {
828 SCH_GROUP* group = static_cast<SCH_GROUP*>( item );
829 wxString groupName = group->GetName();
830
831 if( screenVisits[sheet.LastScreen()] > 1 )
832 groupName = wxString::Format( wxT( "%s (%s)" ), groupName, sheet.PathHumanReadable() );
833
834 XNODE* xgroup; // current symbol being constructed
835 xcomps->AddChild( xgroup = node( wxT( "group" ) ) );
836
837 xgroup->AddAttribute( wxT( "name" ), groupName );
838 xgroup->AddAttribute( wxT( "uuid" ), instancePrefix + group->m_Uuid.AsString() );
839 xgroup->AddAttribute( wxT( "lib_id" ), group->GetDesignBlockLibId().Format() );
840
841 XNODE* xmembers;
842 xgroup->AddChild( xmembers = node( wxT( "members" ) ) );
843
844 for( EDA_ITEM* member : group->GetItems() )
845 {
846 if( member->Type() == SCH_SYMBOL_T )
847 {
848 XNODE* xmember;
849 xmembers->AddChild( xmember = node( wxT( "member" ) ) );
850 xmember->AddAttribute( wxT( "uuid" ), instancePrefix + member->m_Uuid.AsString() );
851 }
852 else if( member->Type() == SCH_GROUP_T )
853 {
854 // Emit nested groups so the board side can rebuild the nesting.
855 XNODE* xmember;
856 xmembers->AddChild( xmember = node( wxT( "member" ) ) );
857 xmember->AddAttribute( wxT( "uuid" ), instancePrefix + member->m_Uuid.AsString() );
858 }
859 else if( member->Type() == SCH_SHEET_T )
860 {
861 SCH_SHEET_PATH subSheetPath = sheet;
862 std::vector<SCH_SHEET_PATH> descendantSheets;
863
864 subSheetPath.push_back( static_cast<SCH_SHEET*>( member ) );
865 sheetList.GetSheetsWithinPath( descendantSheets, subSheetPath );
866
867 for( const SCH_SHEET_PATH& descendantSheet : descendantSheets )
868 {
869 for( SCH_ITEM* descendantItem : descendantSheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
870 {
871 XNODE* xmember;
872 xmembers->AddChild( xmember = node( wxT( "member" ) ) );
873 xmember->AddAttribute( wxT( "uuid" ),
874 descendantSheet.PathAsString() + descendantItem->m_Uuid.AsString() );
875 }
876 }
877 }
878 }
879 }
880 }
881
882 m_schematic->SetCurrentSheet( currentSheet );
883
884 return xcomps;
885}
886
887
889{
890 XNODE* xvariants = node( wxT( "variants" ) );
891
892 std::set<wxString> variantNames = m_schematic->GetVariantNames();
893
894 for( const wxString& variantName : variantNames )
895 {
896 XNODE* xvariant;
897 xvariants->AddChild( xvariant = node( wxT( "variant" ) ) );
898 xvariant->AddAttribute( wxT( "name" ), variantName );
899
900 wxString description = m_schematic->GetVariantDescription( variantName );
901
902 if( !description.IsEmpty() )
903 xvariant->AddAttribute( wxT( "description" ), description );
904 }
905
906 return xvariants;
907}
908
909
911 SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH& aSymbolSheet, const SCH_SHEET_LIST& aSheetList )
912{
913 std::vector<SCH_SHEET_PATH> symbolSheets;
914 symbolSheets.push_back( aSymbolSheet );
915
916 std::unordered_set<wxString> compClassNames = aSymbol->GetComponentClassNames( &aSymbolSheet, m_exportVariant );
917 int primaryUnit = aSymbol->GetUnitSelection( &aSymbolSheet );
918
919 if( aSymbol->GetUnitCount() > 1 )
920 {
921 const wxString ref = aSymbol->GetRef( &aSymbolSheet );
922
923 for( const SCH_SHEET_PATH& sheet : aSheetList )
924 {
925 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
926 {
927 const SCH_SYMBOL* symbol2 = static_cast<SCH_SYMBOL*>( item );
928
929 wxString ref2 = symbol2->GetRef( &sheet );
930 const int otherUnit = symbol2->GetUnitSelection( &sheet );
931
932 if( ref2.CmpNoCase( ref ) != 0 )
933 continue;
934
935 if( otherUnit == primaryUnit )
936 continue;
937
938 symbolSheets.push_back( sheet );
939
940 std::unordered_set<wxString> otherClassNames = symbol2->GetComponentClassNames( &sheet,
942 compClassNames.insert( otherClassNames.begin(), otherClassNames.end() );
943 }
944 }
945 }
946
947 // Add sheet-level component classes
948 for( auto& [sheetPath, sheetCompClasses] : m_sheetComponentClasses )
949 {
950 for( SCH_SHEET_PATH& symbolSheetPath : symbolSheets )
951 {
952 if( symbolSheetPath.IsContainedWithin( sheetPath ) )
953 compClassNames.insert( sheetCompClasses.begin(), sheetCompClasses.end() );
954 }
955 }
956
957 std::vector<wxString> sortedCompClassNames( compClassNames.begin(), compClassNames.end() );
958 std::sort( sortedCompClassNames.begin(), sortedCompClassNames.end(),
959 []( const wxString& str1, const wxString& str2 )
960 {
961 return str1.Cmp( str2 ) < 0;
962 } );
963
964 return sortedCompClassNames;
965}
966
967
969{
970 SCH_SCREEN* screen;
971 XNODE* xdesign = node( wxT( "design" ) );
972 XNODE* xtitleBlock;
973 XNODE* xsheet;
974 XNODE* xcomment;
975 XNODE* xtextvar;
976 wxString sheetTxt;
977 wxFileName sourceFileName;
978
979 // the root sheet is a special sheet, call it source
980 xdesign->AddChild( node( wxT( "source" ), m_schematic->GetFileName() ) );
981
982 xdesign->AddChild( node( wxT( "date" ), GetISO8601CurrentDateTime() ) );
983
984 // which Eeschema tool
985 xdesign->AddChild( node( wxT( "tool" ), wxT( "Eeschema " ) + GetBuildVersion() ) );
986
987 const std::map<wxString, wxString>& properties = m_schematic->Project().GetTextVars();
988
989 for( const std::pair<const wxString, wxString>& prop : properties )
990 {
991 xdesign->AddChild( xtextvar = node( wxT( "textvar" ), prop.second ) );
992 xtextvar->AddAttribute( wxT( "name" ), prop.first );
993 }
994
995 /*
996 * Export the sheets information
997 */
998 unsigned sheetIndex = 1; // Human readable index
999
1000 for( const SCH_SHEET_PATH& sheet : m_exportSheets )
1001 {
1002 screen = sheet.LastScreen();
1003
1004 xdesign->AddChild( xsheet = node( wxT( "sheet" ) ) );
1005
1006 // get the string representation of the sheet index number.
1007 sheetTxt.Printf( wxT( "%u" ), sheetIndex++ );
1008 xsheet->AddAttribute( wxT( "number" ), sheetTxt );
1009 xsheet->AddAttribute( wxT( "name" ), sheet.PathHumanReadable() );
1010 xsheet->AddAttribute( wxT( "tstamps" ), sheet.PathAsString() );
1011
1012 TITLE_BLOCK tb = screen->GetTitleBlock();
1013 PROJECT* prj = &m_schematic->Project();
1014
1015 xsheet->AddChild( xtitleBlock = node( wxT( "title_block" ) ) );
1016
1017 xtitleBlock->AddChild( node( wxT( "title" ), ExpandTextVars( tb.GetTitle(), prj, m_resolveTextVars ) ) );
1018 xtitleBlock->AddChild( node( wxT( "company" ), ExpandTextVars( tb.GetCompany(), prj, m_resolveTextVars ) ) );
1019 xtitleBlock->AddChild( node( wxT( "rev" ), ExpandTextVars( tb.GetRevision(), prj, m_resolveTextVars ) ) );
1020 xtitleBlock->AddChild( node( wxT( "date" ), ExpandTextVars( tb.GetDate(), prj, m_resolveTextVars ) ) );
1021
1022 // We are going to remove the fileName directories.
1023 sourceFileName = wxFileName( screen->GetFileName() );
1024 xtitleBlock->AddChild( node( wxT( "source" ), sourceFileName.GetFullName() ) );
1025
1026 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1027 xcomment->AddAttribute( wxT( "number" ), wxT( "1" ) );
1028 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 0 ), prj, m_resolveTextVars ) );
1029
1030 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1031 xcomment->AddAttribute( wxT( "number" ), wxT( "2" ) );
1032 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 1 ), prj, m_resolveTextVars ) );
1033
1034 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1035 xcomment->AddAttribute( wxT( "number" ), wxT( "3" ) );
1036 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 2 ), prj, m_resolveTextVars ) );
1037
1038 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1039 xcomment->AddAttribute( wxT( "number" ), wxT( "4" ) );
1040 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 3 ), prj, m_resolveTextVars ) );
1041
1042 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1043 xcomment->AddAttribute( wxT( "number" ), wxT( "5" ) );
1044 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 4 ), prj, m_resolveTextVars ) );
1045
1046 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1047 xcomment->AddAttribute( wxT( "number" ), wxT( "6" ) );
1048 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 5 ), prj, m_resolveTextVars ) );
1049
1050 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1051 xcomment->AddAttribute( wxT( "number" ), wxT( "7" ) );
1052 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 6 ), prj, m_resolveTextVars ) );
1053
1054 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1055 xcomment->AddAttribute( wxT( "number" ), wxT( "8" ) );
1056 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 7 ), prj, m_resolveTextVars ) );
1057
1058 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
1059 xcomment->AddAttribute( wxT( "number" ), wxT( "9" ) );
1060 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 8 ), prj, m_resolveTextVars ) );
1061 }
1062
1063 return xdesign;
1064}
1065
1066
1068{
1069 XNODE* xlibs = node( wxT( "libraries" ) ); // auto_ptr
1070 LIBRARY_MANAGER& manager = Pgm().GetLibraryManager();
1071
1072 for( const wxString& libNickname : m_libraries )
1073 {
1074 XNODE* xlibrary;
1075
1076 std::optional<wxString> uri = manager.GetFullURI( LIBRARY_TABLE_TYPE::SYMBOL, libNickname );
1077
1078 if( uri )
1079 {
1080 xlibs->AddChild( xlibrary = node( wxT( "library" ) ) );
1081 xlibrary->AddAttribute( wxT( "logical" ), libNickname );
1082 xlibrary->AddChild( node( wxT( "uri" ), *uri ) );
1083 }
1084 }
1085
1086 return xlibs;
1087}
1088
1089
1091{
1092 XNODE* xlibparts = node( wxT( "libparts" ) ); // auto_ptr
1093 std::vector<SCH_FIELD*> fieldList;
1094
1095 m_libraries.clear();
1096
1097 for( LIB_SYMBOL* lcomp : m_libParts )
1098 {
1099 wxString libNickname = lcomp->GetLibId().GetLibNickname();;
1100
1101 // The library nickname will be empty if the cache library is used.
1102 if( !libNickname.IsEmpty() )
1103 m_libraries.insert( libNickname ); // inserts symbol's library if unique
1104
1105 XNODE* xlibpart;
1106 xlibparts->AddChild( xlibpart = node( wxT( "libpart" ) ) );
1107 xlibpart->AddAttribute( wxT( "lib" ), libNickname );
1108 xlibpart->AddAttribute( wxT( "part" ), lcomp->GetName() );
1109
1110 //----- show the important properties -------------------------
1111 if( !lcomp->GetDescription().IsEmpty() )
1112 xlibpart->AddChild( node( wxT( "description" ), lcomp->GetDescription() ) );
1113
1114 if( !lcomp->GetDatasheetField().GetText().IsEmpty() )
1115 xlibpart->AddChild( node( wxT( "docs" ), lcomp->GetDatasheetField().GetText() ) );
1116
1117 // Write the footprint list
1118 if( lcomp->GetFPFilters().GetCount() )
1119 {
1120 XNODE* xfootprints;
1121 xlibpart->AddChild( xfootprints = node( wxT( "footprints" ) ) );
1122
1123 for( unsigned i = 0; i < lcomp->GetFPFilters().GetCount(); ++i )
1124 {
1125 if( !lcomp->GetFPFilters()[i].IsEmpty() )
1126 xfootprints->AddChild( node( wxT( "fp" ), lcomp->GetFPFilters()[i] ) );
1127 }
1128 }
1129
1130 //----- show the fields here ----------------------------------
1131 fieldList.clear();
1132 lcomp->GetFields( fieldList );
1133
1134 XNODE* xfields;
1135 xlibpart->AddChild( xfields = node( "fields" ) );
1136
1137 for( const SCH_FIELD* field : fieldList )
1138 {
1139 XNODE* xfield;
1140 xfields->AddChild( xfield = node( wxT( "field" ), field->GetText() ) );
1141 xfield->AddAttribute( wxT( "name" ), field->GetUntranslatedName() );
1142 }
1143
1144 //----- show the pins here ------------------------------------
1145 // NOTE: Expand stacked-pin notation into individual pins so downstream
1146 // tools (e.g. CvPcb) see the actual number of footprint pins.
1147 std::vector<SCH_PIN*> pinList = lcomp->GetGraphicalPins( 0, 0 );
1148
1149 /*
1150 * We must erase redundant Pins references in pinList
1151 * These redundant pins exist because some pins are found more than one time when a
1152 * symbol has multiple parts per package or has 2 representations (DeMorgan conversion).
1153 * For instance, a 74ls00 has DeMorgan conversion, with different pin shapes, and
1154 * therefore each pin appears 2 times in the list. Common pins (VCC, GND) can also be
1155 * found more than once.
1156 */
1157 sort( pinList.begin(), pinList.end(), sortPinsByNumber );
1158
1159 for( int ii = 0; ii < (int)pinList.size()-1; ii++ )
1160 {
1161 if( pinList[ii]->GetNumber() == pinList[ii+1]->GetNumber() )
1162 { // 2 pins have the same number, remove the redundant pin at index i+1
1163 pinList.erase(pinList.begin() + ii + 1);
1164 ii--;
1165 }
1166 }
1167
1168 wxLogTrace( "CVPCB_PINCOUNT",
1169 wxString::Format( "makeLibParts: lib='%s' part='%s' pinList(size)=%zu",
1170 libNickname, lcomp->GetName(), pinList.size() ) );
1171
1172 if( pinList.size() )
1173 {
1174 XNODE* pins;
1175
1176 xlibpart->AddChild( pins = node( wxT( "pins" ) ) );
1177
1178 for( unsigned i=0; i<pinList.size(); ++i )
1179 {
1180 SCH_PIN* basePin = pinList[i];
1181
1182 bool stackedValid = false;
1183 std::vector<wxString> expandedNums = basePin->GetStackedPinNumbers( &stackedValid );
1184
1185 // If stacked notation detected and valid, emit one libparts pin per expanded number.
1186 if( stackedValid && !expandedNums.empty() )
1187 {
1188 for( const wxString& num : expandedNums )
1189 {
1190 XNODE* pin;
1191 pins->AddChild( pin = node( wxT( "pin" ) ) );
1192 pin->AddAttribute( wxT( "num" ), num );
1193 pin->AddAttribute( wxT( "name" ), basePin->GetShownName() );
1194 pin->AddAttribute( wxT( "type" ), basePin->GetCanonicalElectricalTypeName() );
1195
1196 wxLogTrace( "CVPCB_PINCOUNT",
1197 wxString::Format( "makeLibParts: -> pin num='%s' name='%s' (expanded)",
1198 num, basePin->GetShownName() ) );
1199 }
1200 }
1201 else
1202 {
1203 XNODE* pin;
1204 pins->AddChild( pin = node( wxT( "pin" ) ) );
1205 pin->AddAttribute( wxT( "num" ), basePin->GetShownNumber() );
1206 pin->AddAttribute( wxT( "name" ), basePin->GetShownName() );
1207 pin->AddAttribute( wxT( "type" ), basePin->GetCanonicalElectricalTypeName() );
1208
1209 wxLogTrace( "CVPCB_PINCOUNT",
1210 wxString::Format( "makeLibParts: -> pin num='%s' name='%s'",
1211 basePin->GetShownNumber(),
1212 basePin->GetShownName() ) );
1213 }
1214
1215 // caution: construction work site here, drive slowly
1216 }
1217 }
1218 }
1219
1220 return xlibparts;
1221}
1222
1223
1225{
1226 wxString netCodeTxt;
1227 XNODE* xnets = node( wxT( "nets" ) ); // auto_ptr if exceptions ever get used.
1228 XNODE* xnet = nullptr;
1229
1230 /* output:
1231 <net code="123" name="/cfcard.sch/WAIT#" class="signal">
1232 <node ref="R23" pin="1"/>
1233 <node ref="U18" pin="12"/>
1234 </net>
1235 */
1236
1237 struct NET_NODE
1238 {
1239 NET_NODE( SCH_PIN* aPin, const SCH_SHEET_PATH& aSheet ) :
1240 m_Pin( aPin ),
1241 m_Sheet( aSheet )
1242 {}
1243
1244 SCH_PIN* m_Pin;
1245 SCH_SHEET_PATH m_Sheet;
1246 };
1247
1248 struct NET_RECORD
1249 {
1250 NET_RECORD( const wxString& aName ) :
1251 m_Name( aName ),
1252 m_HasNoConnect( false )
1253 {};
1254
1255 wxString m_Name;
1256 wxString m_Class;
1257 bool m_HasNoConnect;
1258 std::vector<NET_NODE> m_Nodes;
1259 };
1260
1261 std::vector<NET_RECORD*> nets;
1262
1263 std::shared_ptr<NET_SETTINGS> netSettings;
1264
1265 if( m_schematic )
1266 netSettings = m_schematic->Project().GetProjectFile().NetSettings();
1267
1268 for( const EXPORT_NET& net : m_exportNets )
1269 {
1270 wxString netName = ( aCtl & GNL_OPT_KICAD ) ? net.name : UnescapeString( net.name );
1271 nets.emplace_back( new NET_RECORD( netName ) );
1272 NET_RECORD* net_record = nets.back();
1273 net_record->m_HasNoConnect = net.hasNoConnect;
1274
1275 if( netSettings )
1276 {
1277 if( const auto netclass = netSettings->GetEffectiveNetClass( net.name ) )
1278 net_record->m_Class = UnescapeString( netclass->GetName() );
1279 }
1280
1281 for( const auto& [pin, sheet] : net.pins )
1282 {
1283 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
1284 bool forBOM = aCtl & GNL_OPT_BOM;
1285 bool forBoard = aCtl & GNL_OPT_KICAD;
1286
1287 if( !symbol )
1288 continue;
1289
1290 if( forBOM && ( sheet.GetExcludedFromBOM( m_exportVariant )
1291 || symbol->ResolveExcludedFromBOM( &sheet, m_exportVariant ) ) )
1292 {
1293 continue;
1294 }
1295
1296 if( forBoard && ( sheet.GetExcludedFromBoard( m_exportVariant )
1297 || symbol->ResolveExcludedFromBoard( &sheet, m_exportVariant ) ) )
1298 {
1299 continue;
1300 }
1301
1302 net_record->m_Nodes.emplace_back( pin, sheet );
1303 }
1304 }
1305
1306 // Netlist ordering: Net name, then ref des, then pin name
1307 std::sort( nets.begin(), nets.end(),
1308 []( const NET_RECORD* a, const NET_RECORD*b )
1309 {
1310 return StrNumCmp( a->m_Name, b->m_Name ) < 0;
1311 } );
1312
1313 for( int i = 0; i < (int) nets.size(); ++i )
1314 {
1315 NET_RECORD* net_record = nets[i];
1316 bool added = false;
1317 XNODE* xnode;
1318
1319 // Netlist ordering: Net name, then ref des, then pin name
1320 std::sort( net_record->m_Nodes.begin(), net_record->m_Nodes.end(),
1321 []( const NET_NODE& a, const NET_NODE& b )
1322 {
1323 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
1324 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
1325
1326 if( refA == refB )
1327 return a.m_Pin->GetShownNumber() < b.m_Pin->GetShownNumber();
1328
1329 return refA < refB;
1330 } );
1331
1332 // Some duplicates can exist, for example on multi-unit parts with duplicated pins across
1333 // units. If the user connects the pins on each unit, they will appear on separate
1334 // subgraphs. Remove those here:
1335 alg::remove_duplicates( net_record->m_Nodes,
1336 []( const NET_NODE& a, const NET_NODE& b )
1337 {
1338 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
1339 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
1340
1341 return refA == refB && a.m_Pin->GetShownNumber() == b.m_Pin->GetShownNumber();
1342 } );
1343
1344 // Determine if all pins in the net are stacked (nets with only one pin are implicitly
1345 // taken to be stacked)
1346 bool allNetPinsStacked = true;
1347
1348 if( net_record->m_Nodes.size() > 1 )
1349 {
1350 SCH_PIN* firstPin = net_record->m_Nodes.begin()->m_Pin;
1351 allNetPinsStacked = std::all_of( net_record->m_Nodes.begin() + 1, net_record->m_Nodes.end(),
1352 [=]( NET_NODE& node )
1353 {
1354 return firstPin->GetParent() == node.m_Pin->GetParent()
1355 && firstPin->GetPosition() == node.m_Pin->GetPosition()
1356 && firstPin->GetName() == node.m_Pin->GetName();
1357 } );
1358 }
1359
1360 for( const NET_NODE& netNode : net_record->m_Nodes )
1361 {
1362 wxString refText = netNode.m_Pin->GetParentSymbol()->GetRef( &netNode.m_Sheet );
1363
1364 // Skip power symbols and virtual symbols
1365 if( refText[0] == wxChar( '#' ) )
1366 continue;
1367
1368 // Emit the resolved footprint pad number(s), not the raw symbol pin number, so a
1369 // remapped pin's net lands on the right pad when the board reads this netlist
1370 // (issue #2282). Shared with the PIN_INFO path via resolvePadNumbers.
1371 std::vector<wxString> nums = resolvePadNumbers( netNode.m_Pin, netNode.m_Sheet );
1372
1373 // An unmapped pin contributes no pad, so skip it and do not open an empty net for it.
1374 if( nums.empty() )
1375 continue;
1376
1377 wxString baseName = netNode.m_Pin->GetShownName();
1378 wxString pinType = netNode.m_Pin->GetCanonicalElectricalTypeName();
1379
1380 if( !added )
1381 {
1382 netCodeTxt.Printf( wxT( "%d" ), i + 1 );
1383
1384 xnets->AddChild( xnet = node( wxT( "net" ) ) );
1385 xnet->AddAttribute( wxT( "code" ), netCodeTxt );
1386 xnet->AddAttribute( wxT( "name" ), net_record->m_Name );
1387 xnet->AddAttribute( wxT( "class" ), net_record->m_Class );
1388
1389 added = true;
1390 }
1391
1392 for( const wxString& num : nums )
1393 {
1394 xnet->AddChild( xnode = node( wxT( "node" ) ) );
1395 xnode->AddAttribute( wxT( "ref" ), refText );
1396 xnode->AddAttribute( wxT( "pin" ), num );
1397
1398 wxString fullName = baseName.IsEmpty() ? num : baseName + wxT( "_" ) + num;
1399
1400 if( !baseName.IsEmpty() || nums.size() > 1 )
1401 xnode->AddAttribute( wxT( "pinfunction" ), fullName );
1402
1403 wxString typeAttr = pinType;
1404
1405 if( net_record->m_HasNoConnect
1406 && ( net_record->m_Nodes.size() == 1 || allNetPinsStacked ) )
1407 {
1408 typeAttr += wxT( "+no_connect" );
1409 }
1410
1411 xnode->AddAttribute( wxT( "pintype" ), typeAttr );
1412 }
1413 }
1414 }
1415
1416 for( NET_RECORD* record : nets )
1417 delete record;
1418
1419 return xnets;
1420}
1421
1423{
1424 const auto& committed = m_schematic->NetChains().GetCommittedNetChains();
1425
1426 if( committed.empty() )
1427 return nullptr;
1428
1429 XNODE* xchains = node( wxT( "net_chains" ) );
1430
1431 for( const std::unique_ptr<SCH_NETCHAIN>& chain : committed )
1432 {
1433 if( !chain )
1434 continue;
1435
1436 XNODE* xchain;
1437 xchains->AddChild( xchain = node( wxT( "net_chain" ) ) );
1438 xchain->AddAttribute( wxT( "name" ), chain->GetName() );
1439
1440 if( !chain->GetNetClass().IsEmpty() )
1441 xchain->AddAttribute( wxT( "net_class" ), chain->GetNetClass() );
1442
1443 // Carry the chain's class assignment (from project's NET_SETTINGS) so
1444 // that downstream consumers of the netlist can see chain hierarchy
1445 // without having to also parse the .kicad_pro file.
1446 if( m_schematic )
1447 {
1448 PROJECT_FILE& pf = m_schematic->Project().GetProjectFile();
1449 const std::shared_ptr<NET_SETTINGS>& ns = pf.NetSettings();
1450
1451 if( ns )
1452 {
1453 wxString className = ns->GetNetChainClass( chain->GetName() );
1454
1455 if( !className.IsEmpty() )
1456 xchain->AddAttribute( wxT( "net_chain_class" ), className );
1457 }
1458 }
1459
1460 if( chain->GetColor() != COLOR4D::UNSPECIFIED )
1461 {
1462 const COLOR4D& c = chain->GetColor();
1463 xchain->AddAttribute( wxT( "color" ),
1464 wxString::Format( wxT( "#%02X%02X%02X%02X" ),
1465 (int) std::clamp( KiROUND( c.r * 255.0 ), 0, 255 ),
1466 (int) std::clamp( KiROUND( c.g * 255.0 ), 0, 255 ),
1467 (int) std::clamp( KiROUND( c.b * 255.0 ), 0, 255 ),
1468 (int) std::clamp( KiROUND( c.a * 255.0 ), 0, 255 ) ) );
1469 }
1470
1471 XNODE* xmembers;
1472 xchain->AddChild( xmembers = node( wxT( "members" ) ) );
1473
1474 // Synthetic per-run subgraph names (__SG_*) embed run-specific subgraph codes
1475 // that do not survive a reload, and downstream consumers cannot resolve them
1476 // back to a real net. Mirror the sexpr writer's filter so the XML output is
1477 // limited to nets that have stable, user-visible names.
1478 for( const wxString& net : chain->GetNets() )
1479 {
1480 if( !SCH_NETCHAIN::IsPersistableNet( net ) )
1481 continue;
1482
1483 XNODE* xmember;
1484 xmembers->AddChild( xmember = node( wxT( "member" ) ) );
1485 xmember->AddAttribute( wxT( "net" ), net );
1486 }
1487
1488 if( !chain->GetTerminalRef( 0 ).IsEmpty() || !chain->GetTerminalRef( 1 ).IsEmpty() )
1489 {
1490 XNODE* xterms;
1491 xchain->AddChild( xterms = node( wxT( "terminal_pins" ) ) );
1492
1493 for( int i = 0; i < 2; ++i )
1494 {
1495 if( chain->GetTerminalRef( i ).IsEmpty() )
1496 continue;
1497
1498 XNODE* xterm;
1499 xterms->AddChild( xterm = node( wxT( "terminal_pin" ) ) );
1500 xterm->AddAttribute( wxT( "ref" ), chain->GetTerminalRef( i ) );
1501 xterm->AddAttribute( wxT( "pin" ), chain->GetTerminalPinNum( i ) );
1502 }
1503 }
1504 }
1505
1506 return xchains;
1507}
1508
1509
1510XNODE* NETLIST_EXPORTER_XML::node( const wxString& aName, const wxString& aTextualContent )
1511{
1512 XNODE* n = new XNODE( wxXML_ELEMENT_NODE, aName );
1513
1514 if( aTextualContent.Len() > 0 ) // excludes wxEmptyString, the parameter's default value
1515 n->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) );
1516
1517 return n;
1518}
1519
1520
1521static bool sortPinsByNumber( SCH_PIN* aPin1, SCH_PIN* aPin2 )
1522{
1523 // return "lhs < rhs"
1524 return StrNumCmp( aPin1->GetShownNumber(), aPin2->GetShownNumber(), true ) < 0;
1525}
1526
1527
1529{
1531
1532 SCH_SHEET_LIST sheetList = m_exportSheets;
1533
1534 auto getComponentClassFields =
1535 [&]( const std::vector<SCH_FIELD>& fields, const SCH_SHEET_PATH* sheetPath )
1536 {
1537 std::unordered_set<wxString> componentClasses;
1538
1539 for( const SCH_FIELD& field : fields )
1540 {
1541 if( field.GetUntranslatedName() == wxT( "Component Class" ) )
1542 {
1543 if( field.GetShownText( sheetPath, m_resolveTextVars ) != wxEmptyString )
1544 componentClasses.insert( field.GetShownText( sheetPath, m_resolveTextVars ) );
1545 }
1546 }
1547
1548 return componentClasses;
1549 };
1550
1551 for( const SCH_SHEET_PATH& sheet : sheetList )
1552 {
1553 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
1554 {
1555 SCH_SHEET* sheetItem = static_cast<SCH_SHEET*>( item );
1556 std::unordered_set<wxString> sheetComponentClasses;
1557 const std::unordered_set<SCH_RULE_AREA*>& sheetRuleAreas = sheetItem->GetRuleAreaCache();
1558
1559 for( const SCH_RULE_AREA* ruleArea : sheetRuleAreas )
1560 {
1561 for( const SCH_DIRECTIVE_LABEL* label : ruleArea->GetDirectives() )
1562 {
1563 std::unordered_set<wxString> ruleAreaComponentClasses = getComponentClassFields( label->GetFields(),
1564 &sheet );
1565 sheetComponentClasses.insert( ruleAreaComponentClasses.begin(), ruleAreaComponentClasses.end() );
1566 }
1567 }
1568
1569 SCH_SHEET_PATH newPath = sheet;
1570 newPath.push_back( sheetItem );
1571 wxASSERT( !m_sheetComponentClasses.contains( newPath ) );
1572
1573 m_sheetComponentClasses[newPath] = sheetComponentClasses;
1574 }
1575 }
1576}
const char * name
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
wxString GetBuildVersion()
Get the full KiCad version string.
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
const KIID m_Uuid
Definition eda_item.h:599
Represents an (ordered) list of JUMPER_GROUP objects.
const std::vector< JUMPER_GROUP > & GetAll() const
bool IsEmpty() const
Represents a group of jumper pins or pads, keyed by name.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:391
double g
Green component.
Definition color4d.h:392
double a
Alpha component.
Definition color4d.h:394
double b
Blue component.
Definition color4d.h:393
wxString AsString() const
Definition kiid.cpp:264
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false)
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:108
const wxString GetUniStringLibNickname() const
Definition lib_id.h:84
Define a library symbol object.
Definition lib_symbol.h:114
SCH_SYMBOL * findNextSymbol(EDA_ITEM *aItem, const SCH_SHEET_PATH &aSheetPath)
Check if the given symbol should be processed for netlisting.
std::set< LIB_SYMBOL *, LIB_SYMBOL_LESS_THAN > m_libParts
unique library symbols used. LIB_SYMBOL items are sorted by names
std::vector< wxString > resolvePadNumbers(const SCH_PIN *aPin, const SCH_SHEET_PATH &aSheetPath) const
Resolve aPin to its effective pad number(s) for every netlist path (issue #2282).
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
std::vector< EXPORT_NET > m_exportNets
wxString m_exportVariant
The schematic we're generating a netlist for.
std::map< SCH_SHEET_PATH, std::unordered_set< wxString > > m_sheetComponentClasses
Map of all sheets to component classes covering the whole sheet.
XNODE * makeDesignHeader()
Fill out a project "design" header into an XML node.
XNODE * makeLibraries()
Fill out an XML node with a list of used libraries and returns it.
XNODE * node(const wxString &aName, const wxString &aTextualContent=wxEmptyString)
A convenience function that creates a new XNODE with an optional textual child.
XNODE * makeListOfNets(unsigned aCtl)
Fill out an XML node with a list of nets and returns it.
void addSymbolFields(XNODE *aNode, SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aSheet, const SCH_SHEET_LIST &aSheetList)
Holder for multi-unit symbol fields.
RESOLUTION_CONTEXT m_resolveTextVars
bool writeNetlist(const wxString &aOutFileName, unsigned aNetlistOptions, REPORTER &aReporter) override
Write generic netlist to aOutFileName.
std::vector< wxString > getComponentClassNamesForAllSymbolUnits(SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aSymbolSheet, const SCH_SHEET_LIST &aSheetList)
Finds all component class names attached to any sub-unit of a given symbol.
XNODE * makeSymbols(unsigned aCtl)
XNODE * makeRoot(unsigned aCtl=GNL_ALL)
Build the entire document tree for the generic export.
std::set< wxString > m_libraries
XNODE * makeLibParts()
Fill out an XML node with the unique library parts and returns it.
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:125
The backing store for a PROJECT, in JSON format.
std::shared_ptr< NET_SETTINGS > & NetSettings()
Container for project specific data.
Definition project.h:63
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
wxString GetShownText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString, int aDepth=0) const
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:48
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
bool ResolveExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:428
const std::unordered_set< SCH_RULE_AREA * > & GetRuleAreaCache() const
Get the cache of rule areas enclosing this item.
Definition sch_item.h:738
bool ResolveExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:392
wxString ResolveText(const wxString &aText, const SCH_SHEET_PATH *aPath, int aDepth=0) const
Definition sch_item.cpp:464
bool ResolveExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:410
bool ResolveDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:446
bool ResolveExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:374
static bool IsPersistableNet(const wxString &aNet)
Synthetic keys do not survive a reload, so only named nets are written out.
std::vector< wxString > GetStackedPinNumbers(bool *aValid=nullptr) const
Definition sch_pin.cpp:714
wxString GetCanonicalElectricalTypeName() const
Definition sch_pin.cpp:467
const wxString & GetShownName() const
Definition sch_pin.cpp:697
const wxString & GetShownNumber() const
Definition sch_pin.cpp:708
const wxString & GetFileName() const
Definition sch_screen.h:157
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:168
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void GetSheetsWithinPath(std::vector< SCH_SHEET_PATH > &aSheets, const SCH_SHEET_PATH &aSheetPath) const
Add a SCH_SHEET_PATH object to aSheets for each sheet in the list that are contained within aSheetPat...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
Variant information for a schematic symbol.
std::optional< LIB_ID > m_SymbolOverride
Alternate library symbol to substitute for the base symbol in this variant, if any.
Schematic symbol object.
Definition sch_symbol.h:75
bool UseLibIdLookup() const
Definition sch_symbol.h:182
wxString GetSchSymbolLibraryName() const
const wxString GetFootprintFieldText(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, const wxString &aVariantName=wxEmptyString) const
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
wxString GetShownDescription(RESOLUTION_CONTEXT aContext, int aDepth=0) const override
std::unordered_set< wxString > GetComponentClassNames(const SCH_SHEET_PATH *aPath, const wxString &aVariantName) const
Return the component classes this symbol belongs in.
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:165
LIB_SYMBOL * GetVariantLibSymbol(const wxString &aVariantName, const SCH_SHEET_PATH &aPath) const
Resolve the alternate library symbol for a given variant.
bool GetInstance(SCH_SYMBOL_INSTANCE &aInstance, const KIID_PATH &aSheetPath, bool aTestFromEnd=false) const
const wxString GetValue(const SCH_SHEET_PATH *aPath, RESOLUTION_CONTEXT aContext, 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.
int GetUnitCount() const override
Return the number of units per package of the symbol.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:184
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
wxString GetFieldText(const SCH_FIELD *aField, const wxString &aFieldName, const SCH_SHEET_PATH *aPath, const wxString &aVariantName) const
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:38
const wxString & GetCompany() const
Definition title_block.h:93
const wxString & GetRevision() const
Definition title_block.h:83
const wxString & GetDate() const
Definition title_block.h:73
const wxString & GetComment(int aIdx) const
const wxString & GetTitle() const
Definition title_block.h:60
std::map< wxString, wxString > m_Fields
An extension of wxXmlNode that can format its contents as KiCad-style s-expressions.
Definition xnode.h:67
void AddAttribute(const wxString &aName, const wxString &aValue) override
Definition xnode.cpp:88
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, RESOLUTION_CONTEXT aContext)
Definition common.cpp:60
@ RAW_VALUE
Definition common.h:98
void remove_duplicates(_Container &__c)
Deletes all duplicate values from __c.
Definition kicad_algo.h:157
static bool sortPinsByNumber(SCH_PIN *aPin1, SCH_PIN *aPin2)
#define GNL_ALL
@ GNL_LIBRARIES
@ GNL_OPT_KICAD
@ GNL_SYMBOLS
@ GNL_OPT_BOM
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
static const std::vector< std::string > filters
Definition plugin.cpp:64
Class to handle a set of SCH_ITEMs.
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString UnescapeString(const wxString &aSource)
wxString GetISO8601CurrentDateTime()
std::vector< wxString > m_pinNumbers
Definition lib_symbol.h:729
One refdes -> net membership row collected from the NETDEF section.
A simple container for schematic symbol instance information.
std::map< wxString, SCH_SYMBOL_VARIANT > m_Variants
A list of symbol variants.
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ UNTRANSLATED
KIBIS_PIN * pin
const SHAPE_LINE_CHAIN chain
wxLogTrace helper definitions.
@ SCH_GROUP_T
Definition typeinfo.h:169
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:171