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