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