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