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_SHEET_T )
743 {
744 SCH_SHEET_PATH subSheetPath = sheet;
745 std::vector<SCH_SHEET_PATH> descendantSheets;
746
747 subSheetPath.push_back( static_cast<SCH_SHEET*>( member ) );
748 sheetList.GetSheetsWithinPath( descendantSheets, subSheetPath );
749
750 for( const SCH_SHEET_PATH& descendantSheet : descendantSheets )
751 {
752 for( SCH_ITEM* descendantItem : descendantSheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
753 {
754 XNODE* xmember;
755 xmembers->AddChild( xmember = node( wxT( "member" ) ) );
756 xmember->AddAttribute( wxT( "uuid" ), descendantItem->m_Uuid.AsString() );
757 }
758 }
759 }
760 }
761 }
762 }
763
764 m_schematic->SetCurrentSheet( currentSheet );
765
766 return xcomps;
767}
768
769
771{
772 XNODE* xvariants = node( wxT( "variants" ) );
773
774 std::set<wxString> variantNames = m_schematic->GetVariantNames();
775
776 for( const wxString& variantName : variantNames )
777 {
778 XNODE* xvariant;
779 xvariants->AddChild( xvariant = node( wxT( "variant" ) ) );
780 xvariant->AddAttribute( wxT( "name" ), variantName );
781
782 wxString description = m_schematic->GetVariantDescription( variantName );
783
784 if( !description.IsEmpty() )
785 xvariant->AddAttribute( wxT( "description" ), description );
786 }
787
788 return xvariants;
789}
790
791
793 SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH& aSymbolSheet, const SCH_SHEET_LIST& aSheetList )
794{
795 std::vector<SCH_SHEET_PATH> symbolSheets;
796 symbolSheets.push_back( aSymbolSheet );
797
798 std::unordered_set<wxString> compClassNames = aSymbol->GetComponentClassNames( &aSymbolSheet );
799 int primaryUnit = aSymbol->GetUnitSelection( &aSymbolSheet );
800
801 if( aSymbol->GetUnitCount() > 1 )
802 {
803 const wxString ref = aSymbol->GetRef( &aSymbolSheet );
804
805 for( const SCH_SHEET_PATH& sheet : aSheetList )
806 {
807 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
808 {
809 const SCH_SYMBOL* symbol2 = static_cast<SCH_SYMBOL*>( item );
810
811 wxString ref2 = symbol2->GetRef( &sheet );
812 const int otherUnit = symbol2->GetUnitSelection( &sheet );
813
814 if( ref2.CmpNoCase( ref ) != 0 )
815 continue;
816
817 if( otherUnit == primaryUnit )
818 continue;
819
820 symbolSheets.push_back( sheet );
821
822 std::unordered_set<wxString> otherClassNames =
823 symbol2->GetComponentClassNames( &sheet );
824 compClassNames.insert( otherClassNames.begin(), otherClassNames.end() );
825 }
826 }
827 }
828
829 // Add sheet-level component classes
830 for( auto& [sheetPath, sheetCompClasses] : m_sheetComponentClasses )
831 {
832 for( SCH_SHEET_PATH& symbolSheetPath : symbolSheets )
833 {
834 if( symbolSheetPath.IsContainedWithin( sheetPath ) )
835 {
836 compClassNames.insert( sheetCompClasses.begin(), sheetCompClasses.end() );
837 }
838 }
839 }
840
841 std::vector<wxString> sortedCompClassNames( compClassNames.begin(), compClassNames.end() );
842 std::sort( sortedCompClassNames.begin(), sortedCompClassNames.end(),
843 []( const wxString& str1, const wxString& str2 )
844 {
845 return str1.Cmp( str2 ) < 0;
846 } );
847
848 return sortedCompClassNames;
849}
850
851
853{
854 SCH_SCREEN* screen;
855 XNODE* xdesign = node( wxT( "design" ) );
856 XNODE* xtitleBlock;
857 XNODE* xsheet;
858 XNODE* xcomment;
859 XNODE* xtextvar;
860 wxString sheetTxt;
861 wxFileName sourceFileName;
862
863 // the root sheet is a special sheet, call it source
864 xdesign->AddChild( node( wxT( "source" ), m_schematic->GetFileName() ) );
865
866 xdesign->AddChild( node( wxT( "date" ), GetISO8601CurrentDateTime() ) );
867
868 // which Eeschema tool
869 xdesign->AddChild( node( wxT( "tool" ), wxT( "Eeschema " ) + GetBuildVersion() ) );
870
871 const std::map<wxString, wxString>& properties = m_schematic->Project().GetTextVars();
872
873 for( const std::pair<const wxString, wxString>& prop : properties )
874 {
875 xdesign->AddChild( xtextvar = node( wxT( "textvar" ), prop.second ) );
876 xtextvar->AddAttribute( wxT( "name" ), prop.first );
877 }
878
879 /*
880 * Export the sheets information
881 */
882 unsigned sheetIndex = 1; // Human readable index
883
884 for( const SCH_SHEET_PATH& sheet : m_schematic->Hierarchy() )
885 {
886 screen = sheet.LastScreen();
887
888 xdesign->AddChild( xsheet = node( wxT( "sheet" ) ) );
889
890 // get the string representation of the sheet index number.
891 sheetTxt.Printf( wxT( "%u" ), sheetIndex++ );
892 xsheet->AddAttribute( wxT( "number" ), sheetTxt );
893 xsheet->AddAttribute( wxT( "name" ), sheet.PathHumanReadable() );
894 xsheet->AddAttribute( wxT( "tstamps" ), sheet.PathAsString() );
895
896 TITLE_BLOCK tb = screen->GetTitleBlock();
897 PROJECT* prj = &m_schematic->Project();
898
899 xsheet->AddChild( xtitleBlock = node( wxT( "title_block" ) ) );
900
901 xtitleBlock->AddChild( node( wxT( "title" ), ExpandTextVars( tb.GetTitle(), prj ) ) );
902 xtitleBlock->AddChild( node( wxT( "company" ), ExpandTextVars( tb.GetCompany(), prj ) ) );
903 xtitleBlock->AddChild( node( wxT( "rev" ), ExpandTextVars( tb.GetRevision(), prj ) ) );
904 xtitleBlock->AddChild( node( wxT( "date" ), ExpandTextVars( tb.GetDate(), prj ) ) );
905
906 // We are going to remove the fileName directories.
907 sourceFileName = wxFileName( screen->GetFileName() );
908 xtitleBlock->AddChild( node( wxT( "source" ), sourceFileName.GetFullName() ) );
909
910 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
911 xcomment->AddAttribute( wxT( "number" ), wxT( "1" ) );
912 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 0 ), prj ) );
913
914 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
915 xcomment->AddAttribute( wxT( "number" ), wxT( "2" ) );
916 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 1 ), prj ) );
917
918 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
919 xcomment->AddAttribute( wxT( "number" ), wxT( "3" ) );
920 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 2 ), prj ) );
921
922 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
923 xcomment->AddAttribute( wxT( "number" ), wxT( "4" ) );
924 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 3 ), prj ) );
925
926 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
927 xcomment->AddAttribute( wxT( "number" ), wxT( "5" ) );
928 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 4 ), prj ) );
929
930 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
931 xcomment->AddAttribute( wxT( "number" ), wxT( "6" ) );
932 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 5 ), prj ) );
933
934 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
935 xcomment->AddAttribute( wxT( "number" ), wxT( "7" ) );
936 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 6 ), prj ) );
937
938 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
939 xcomment->AddAttribute( wxT( "number" ), wxT( "8" ) );
940 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 7 ), prj ) );
941
942 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
943 xcomment->AddAttribute( wxT( "number" ), wxT( "9" ) );
944 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 8 ), prj ) );
945 }
946
947 return xdesign;
948}
949
950
952{
953 XNODE* xlibs = node( wxT( "libraries" ) ); // auto_ptr
955
956 for( std::set<wxString>::iterator it = m_libraries.begin(); it!=m_libraries.end(); ++it )
957 {
958 wxString libNickname = *it;
959 XNODE* xlibrary;
960
961 std::optional<wxString> uri = manager.GetFullURI( LIBRARY_TABLE_TYPE::SYMBOL, libNickname );
962
963 if( uri )
964 {
965 xlibs->AddChild( xlibrary = node( wxT( "library" ) ) );
966 xlibrary->AddAttribute( wxT( "logical" ), libNickname );
967 xlibrary->AddChild( node( wxT( "uri" ), *uri ) );
968 }
969
970 // @todo: add more fun stuff here
971 }
972
973 return xlibs;
974}
975
976
978{
979 XNODE* xlibparts = node( wxT( "libparts" ) ); // auto_ptr
980 std::vector<SCH_FIELD*> fieldList;
981
982 m_libraries.clear();
983
984 for( LIB_SYMBOL* lcomp : m_libParts )
985 {
986 wxString libNickname = lcomp->GetLibId().GetLibNickname();;
987
988 // The library nickname will be empty if the cache library is used.
989 if( !libNickname.IsEmpty() )
990 m_libraries.insert( libNickname ); // inserts symbol's library if unique
991
992 XNODE* xlibpart;
993 xlibparts->AddChild( xlibpart = node( wxT( "libpart" ) ) );
994 xlibpart->AddAttribute( wxT( "lib" ), libNickname );
995 xlibpart->AddAttribute( wxT( "part" ), lcomp->GetName() );
996
997 //----- show the important properties -------------------------
998 if( !lcomp->GetDescription().IsEmpty() )
999 xlibpart->AddChild( node( wxT( "description" ), lcomp->GetDescription() ) );
1000
1001 if( !lcomp->GetDatasheetField().GetText().IsEmpty() )
1002 xlibpart->AddChild( node( wxT( "docs" ), lcomp->GetDatasheetField().GetText() ) );
1003
1004 // Write the footprint list
1005 if( lcomp->GetFPFilters().GetCount() )
1006 {
1007 XNODE* xfootprints;
1008 xlibpart->AddChild( xfootprints = node( wxT( "footprints" ) ) );
1009
1010 for( unsigned i = 0; i < lcomp->GetFPFilters().GetCount(); ++i )
1011 {
1012 if( !lcomp->GetFPFilters()[i].IsEmpty() )
1013 xfootprints->AddChild( node( wxT( "fp" ), lcomp->GetFPFilters()[i] ) );
1014 }
1015 }
1016
1017 //----- show the fields here ----------------------------------
1018 fieldList.clear();
1019 lcomp->GetFields( fieldList );
1020
1021 XNODE* xfields;
1022 xlibpart->AddChild( xfields = node( "fields" ) );
1023
1024 for( const SCH_FIELD* field : fieldList )
1025 {
1026 XNODE* xfield;
1027 xfields->AddChild( xfield = node( wxT( "field" ), field->GetText() ) );
1028 xfield->AddAttribute( wxT( "name" ), field->GetCanonicalName() );
1029 }
1030
1031 //----- show the pins here ------------------------------------
1032 // NOTE: Expand stacked-pin notation into individual pins so downstream
1033 // tools (e.g. CvPcb) see the actual number of footprint pins.
1034 std::vector<SCH_PIN*> pinList = lcomp->GetGraphicalPins( 0, 0 );
1035
1036 /*
1037 * We must erase redundant Pins references in pinList
1038 * These redundant pins exist because some pins are found more than one time when a
1039 * symbol has multiple parts per package or has 2 representations (DeMorgan conversion).
1040 * For instance, a 74ls00 has DeMorgan conversion, with different pin shapes, and
1041 * therefore each pin appears 2 times in the list. Common pins (VCC, GND) can also be
1042 * found more than once.
1043 */
1044 sort( pinList.begin(), pinList.end(), sortPinsByNumber );
1045
1046 for( int ii = 0; ii < (int)pinList.size()-1; ii++ )
1047 {
1048 if( pinList[ii]->GetNumber() == pinList[ii+1]->GetNumber() )
1049 { // 2 pins have the same number, remove the redundant pin at index i+1
1050 pinList.erase(pinList.begin() + ii + 1);
1051 ii--;
1052 }
1053 }
1054
1055 wxLogTrace( "CVPCB_PINCOUNT",
1056 wxString::Format( "makeLibParts: lib='%s' part='%s' pinList(size)=%zu",
1057 libNickname, lcomp->GetName(), pinList.size() ) );
1058
1059 if( pinList.size() )
1060 {
1061 XNODE* pins;
1062
1063 xlibpart->AddChild( pins = node( wxT( "pins" ) ) );
1064
1065 for( unsigned i=0; i<pinList.size(); ++i )
1066 {
1067 SCH_PIN* basePin = pinList[i];
1068
1069 bool stackedValid = false;
1070 std::vector<wxString> expandedNums = basePin->GetStackedPinNumbers( &stackedValid );
1071
1072 // If stacked notation detected and valid, emit one libparts pin per expanded number.
1073 if( stackedValid && !expandedNums.empty() )
1074 {
1075 for( const wxString& num : expandedNums )
1076 {
1077 XNODE* pin;
1078 pins->AddChild( pin = node( wxT( "pin" ) ) );
1079 pin->AddAttribute( wxT( "num" ), num );
1080 pin->AddAttribute( wxT( "name" ), basePin->GetShownName() );
1081 pin->AddAttribute( wxT( "type" ), basePin->GetCanonicalElectricalTypeName() );
1082
1083 wxLogTrace( "CVPCB_PINCOUNT",
1084 wxString::Format( "makeLibParts: -> pin num='%s' name='%s' (expanded)",
1085 num, basePin->GetShownName() ) );
1086 }
1087 }
1088 else
1089 {
1090 XNODE* pin;
1091 pins->AddChild( pin = node( wxT( "pin" ) ) );
1092 pin->AddAttribute( wxT( "num" ), basePin->GetShownNumber() );
1093 pin->AddAttribute( wxT( "name" ), basePin->GetShownName() );
1094 pin->AddAttribute( wxT( "type" ), basePin->GetCanonicalElectricalTypeName() );
1095
1096 wxLogTrace( "CVPCB_PINCOUNT",
1097 wxString::Format( "makeLibParts: -> pin num='%s' name='%s'",
1098 basePin->GetShownNumber(),
1099 basePin->GetShownName() ) );
1100 }
1101
1102 // caution: construction work site here, drive slowly
1103 }
1104 }
1105 }
1106
1107 return xlibparts;
1108}
1109
1110
1112{
1113 wxString netCodeTxt;
1114 XNODE* xnets = node( wxT( "nets" ) ); // auto_ptr if exceptions ever get used.
1115 XNODE* xnet = nullptr;
1116
1117 /* output:
1118 <net code="123" name="/cfcard.sch/WAIT#" class="signal">
1119 <node ref="R23" pin="1"/>
1120 <node ref="U18" pin="12"/>
1121 </net>
1122 */
1123
1124 struct NET_NODE
1125 {
1126 NET_NODE( SCH_PIN* aPin, const SCH_SHEET_PATH& aSheet ) :
1127 m_Pin( aPin ),
1128 m_Sheet( aSheet )
1129 {}
1130
1131 SCH_PIN* m_Pin;
1132 SCH_SHEET_PATH m_Sheet;
1133 };
1134
1135 struct NET_RECORD
1136 {
1137 NET_RECORD( const wxString& aName ) :
1138 m_Name( aName ),
1139 m_HasNoConnect( false )
1140 {};
1141
1142 wxString m_Name;
1143 wxString m_Class;
1144 bool m_HasNoConnect;
1145 std::vector<NET_NODE> m_Nodes;
1146 };
1147
1148 std::vector<NET_RECORD*> nets;
1149
1150 std::shared_ptr<NET_SETTINGS> netSettings;
1151
1152 if( m_schematic )
1153 netSettings = m_schematic->Project().GetProjectFile().NetSettings();
1154
1155 for( const auto& [ key, subgraphs ] : m_schematic->ConnectionGraph()->GetNetMap() )
1156 {
1157 wxString net_name = key.Name;
1158 NET_RECORD* net_record = nullptr;
1159
1160 if( !( aCtl & GNL_OPT_KICAD ) )
1161 net_name = UnescapeString( net_name );
1162
1163 if( subgraphs.empty() )
1164 continue;
1165
1166 nets.emplace_back( new NET_RECORD( net_name ) );
1167 net_record = nets.back();
1168
1169 // Resolve the effective netclass by net name through NET_SETTINGS. This matches
1170 // the lookup used by the schematic painter and avoids relying on the subgraph's
1171 // driver item, which is not set for bus-member subgraphs and which falls back to
1172 // the schematic's current sheet path when looking up its connection (the exporter
1173 // is not tied to any particular sheet view).
1174 if( netSettings )
1175 {
1176 std::shared_ptr<NETCLASS> nc = netSettings->GetEffectiveNetClass( key.Name );
1177
1178 if( nc )
1179 net_record->m_Class = UnescapeString( nc->GetName() );
1180 }
1181
1182 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
1183 {
1184 bool nc = subgraph->GetNoConnect() && subgraph->GetNoConnect()->Type() == SCH_NO_CONNECT_T;
1185 const SCH_SHEET_PATH& sheet = subgraph->GetSheet();
1186
1187 if( nc )
1188 net_record->m_HasNoConnect = true;
1189
1190 for( SCH_ITEM* item : subgraph->GetItems() )
1191 {
1192 if( item->Type() == SCH_PIN_T )
1193 {
1194 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
1195 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
1196 bool forBOM = aCtl & GNL_OPT_BOM;
1197 bool forBoard = aCtl & GNL_OPT_KICAD;
1198
1199 if( !symbol )
1200 continue;
1201
1202 if( forBOM && ( sheet.GetExcludedFromBOM() || symbol->ResolveExcludedFromBOM() ) )
1203 continue;
1204
1205 if( forBoard && ( sheet.GetExcludedFromBoard() || symbol->ResolveExcludedFromBoard() ) )
1206 continue;
1207
1208 net_record->m_Nodes.emplace_back( pin, sheet );
1209 }
1210 }
1211 }
1212 }
1213
1214 // Netlist ordering: Net name, then ref des, then pin name
1215 std::sort( nets.begin(), nets.end(),
1216 []( const NET_RECORD* a, const NET_RECORD*b )
1217 {
1218 return StrNumCmp( a->m_Name, b->m_Name ) < 0;
1219 } );
1220
1221 for( int i = 0; i < (int) nets.size(); ++i )
1222 {
1223 NET_RECORD* net_record = nets[i];
1224 bool added = false;
1225 XNODE* xnode;
1226
1227 // Netlist ordering: Net name, then ref des, then pin name
1228 std::sort( net_record->m_Nodes.begin(), net_record->m_Nodes.end(),
1229 []( const NET_NODE& a, const NET_NODE& b )
1230 {
1231 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
1232 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
1233
1234 if( refA == refB )
1235 return a.m_Pin->GetShownNumber() < b.m_Pin->GetShownNumber();
1236
1237 return refA < refB;
1238 } );
1239
1240 // Some duplicates can exist, for example on multi-unit parts with duplicated pins across
1241 // units. If the user connects the pins on each unit, they will appear on separate
1242 // subgraphs. Remove those here:
1243 alg::remove_duplicates( net_record->m_Nodes,
1244 []( const NET_NODE& a, const NET_NODE& b )
1245 {
1246 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
1247 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
1248
1249 return refA == refB && a.m_Pin->GetShownNumber() == b.m_Pin->GetShownNumber();
1250 } );
1251
1252 // Determine if all pins in the net are stacked (nets with only one pin are implicitly
1253 // taken to be stacked)
1254 bool allNetPinsStacked = true;
1255
1256 if( net_record->m_Nodes.size() > 1 )
1257 {
1258 SCH_PIN* firstPin = net_record->m_Nodes.begin()->m_Pin;
1259 allNetPinsStacked =
1260 std::all_of( net_record->m_Nodes.begin() + 1, net_record->m_Nodes.end(),
1261 [=]( auto& node )
1262 {
1263 return firstPin->GetParent() == node.m_Pin->GetParent()
1264 && firstPin->GetPosition() == node.m_Pin->GetPosition()
1265 && firstPin->GetName() == node.m_Pin->GetName();
1266 } );
1267 }
1268
1269 for( const NET_NODE& netNode : net_record->m_Nodes )
1270 {
1271 wxString refText = netNode.m_Pin->GetParentSymbol()->GetRef( &netNode.m_Sheet );
1272
1273 // Skip power symbols and virtual symbols
1274 if( refText[0] == wxChar( '#' ) )
1275 continue;
1276
1277 if( !added )
1278 {
1279 netCodeTxt.Printf( wxT( "%d" ), i + 1 );
1280
1281 xnets->AddChild( xnet = node( wxT( "net" ) ) );
1282 xnet->AddAttribute( wxT( "code" ), netCodeTxt );
1283 xnet->AddAttribute( wxT( "name" ), net_record->m_Name );
1284 xnet->AddAttribute( wxT( "class" ), net_record->m_Class );
1285
1286 added = true;
1287 }
1288
1289 std::vector<wxString> nums = netNode.m_Pin->GetStackedPinNumbers();
1290 wxString baseName = netNode.m_Pin->GetShownName();
1291 wxString pinType = netNode.m_Pin->GetCanonicalElectricalTypeName();
1292
1293 wxLogTrace( traceStackedPins,
1294 wxString::Format( "XML: net='%s' ref='%s' base='%s' shownNum='%s' expand=%zu",
1295 net_record->m_Name, refText, baseName,
1296 netNode.m_Pin->GetShownNumber(), nums.size() ) );
1297
1298 for( const wxString& num : nums )
1299 {
1300 xnet->AddChild( xnode = node( wxT( "node" ) ) );
1301 xnode->AddAttribute( wxT( "ref" ), refText );
1302 xnode->AddAttribute( wxT( "pin" ), num );
1303
1304 wxString fullName = baseName.IsEmpty() ? num : baseName + wxT( "_" ) + num;
1305
1306 if( !baseName.IsEmpty() || nums.size() > 1 )
1307 xnode->AddAttribute( wxT( "pinfunction" ), fullName );
1308
1309 wxString typeAttr = pinType;
1310
1311 if( net_record->m_HasNoConnect
1312 && ( net_record->m_Nodes.size() == 1 || allNetPinsStacked ) )
1313 {
1314 typeAttr += wxT( "+no_connect" );
1315 wxLogTrace( traceStackedPins,
1316 wxString::Format( "XML: marking node ref='%s' pin='%s' as no_connect",
1317 refText, num ) );
1318 }
1319
1320 xnode->AddAttribute( wxT( "pintype" ), typeAttr );
1321 }
1322 }
1323 }
1324
1325 for( NET_RECORD* record : nets )
1326 delete record;
1327
1328 return xnets;
1329}
1330
1332{
1333 const auto& committed = m_schematic->ConnectionGraph()->GetCommittedNetChains();
1334
1335 if( committed.empty() )
1336 return nullptr;
1337
1338 XNODE* xchains = node( wxT( "net_chains" ) );
1339
1340 for( const std::unique_ptr<SCH_NETCHAIN>& chain : committed )
1341 {
1342 if( !chain )
1343 continue;
1344
1345 XNODE* xchain;
1346 xchains->AddChild( xchain = node( wxT( "net_chain" ) ) );
1347 xchain->AddAttribute( wxT( "name" ), chain->GetName() );
1348
1349 if( !chain->GetNetClass().IsEmpty() )
1350 xchain->AddAttribute( wxT( "net_class" ), chain->GetNetClass() );
1351
1352 // Carry the chain's class assignment (from project's NET_SETTINGS) so
1353 // that downstream consumers of the netlist can see chain hierarchy
1354 // without having to also parse the .kicad_pro file.
1355 if( m_schematic )
1356 {
1357 PROJECT_FILE& pf = m_schematic->Project().GetProjectFile();
1358 const std::shared_ptr<NET_SETTINGS>& ns = pf.NetSettings();
1359
1360 if( ns )
1361 {
1362 wxString className = ns->GetNetChainClass( chain->GetName() );
1363
1364 if( !className.IsEmpty() )
1365 xchain->AddAttribute( wxT( "net_chain_class" ), className );
1366 }
1367 }
1368
1369 if( chain->GetColor() != COLOR4D::UNSPECIFIED )
1370 {
1371 const COLOR4D& c = chain->GetColor();
1372 xchain->AddAttribute( wxT( "color" ),
1373 wxString::Format( wxT( "#%02X%02X%02X%02X" ),
1374 (int) std::clamp( KiROUND( c.r * 255.0 ), 0, 255 ),
1375 (int) std::clamp( KiROUND( c.g * 255.0 ), 0, 255 ),
1376 (int) std::clamp( KiROUND( c.b * 255.0 ), 0, 255 ),
1377 (int) std::clamp( KiROUND( c.a * 255.0 ), 0, 255 ) ) );
1378 }
1379
1380 XNODE* xmembers;
1381 xchain->AddChild( xmembers = node( wxT( "members" ) ) );
1382
1383 // Synthetic per-run subgraph names (__SG_*) embed run-specific subgraph codes
1384 // that do not survive a reload, and downstream consumers cannot resolve them
1385 // back to a real net. Mirror the sexpr writer's filter so the XML output is
1386 // limited to nets that have stable, user-visible names.
1387 for( const wxString& net : chain->GetNets() )
1388 {
1389 if( net.IsEmpty() || net.StartsWith( SCH_NETCHAIN::SYNTHETIC_NET_PREFIX ) )
1390 continue;
1391
1392 XNODE* xmember;
1393 xmembers->AddChild( xmember = node( wxT( "member" ) ) );
1394 xmember->AddAttribute( wxT( "net" ), net );
1395 }
1396
1397 if( !chain->GetTerminalRef( 0 ).IsEmpty() || !chain->GetTerminalRef( 1 ).IsEmpty() )
1398 {
1399 XNODE* xterms;
1400 xchain->AddChild( xterms = node( wxT( "terminal_pins" ) ) );
1401
1402 for( int i = 0; i < 2; ++i )
1403 {
1404 if( chain->GetTerminalRef( i ).IsEmpty() )
1405 continue;
1406
1407 XNODE* xterm;
1408 xterms->AddChild( xterm = node( wxT( "terminal_pin" ) ) );
1409 xterm->AddAttribute( wxT( "ref" ), chain->GetTerminalRef( i ) );
1410 xterm->AddAttribute( wxT( "pin" ), chain->GetTerminalPinNum( i ) );
1411 }
1412 }
1413 }
1414
1415 return xchains;
1416}
1417
1418
1419XNODE* NETLIST_EXPORTER_XML::node( const wxString& aName,
1420 const wxString& aTextualContent /* = wxEmptyString*/ )
1421{
1422 XNODE* n = new XNODE( wxXML_ELEMENT_NODE, aName );
1423
1424 if( aTextualContent.Len() > 0 ) // excludes wxEmptyString, the parameter's default value
1425 n->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) );
1426
1427 return n;
1428}
1429
1430
1431static bool sortPinsByNumber( SCH_PIN* aPin1, SCH_PIN* aPin2 )
1432{
1433 // return "lhs < rhs"
1434 return StrNumCmp( aPin1->GetShownNumber(), aPin2->GetShownNumber(), true ) < 0;
1435}
1437{
1439
1440 SCH_SHEET_LIST sheetList = m_schematic->Hierarchy();
1441
1442 auto getComponentClassFields = [&]( const std::vector<SCH_FIELD>& fields, const SCH_SHEET_PATH* sheetPath )
1443 {
1444 std::unordered_set<wxString> componentClasses;
1445
1446 for( const SCH_FIELD& field : fields )
1447 {
1448 if( field.GetCanonicalName() == wxT( "Component Class" ) )
1449 {
1450 if( field.GetShownText( sheetPath, false ) != wxEmptyString )
1451 componentClasses.insert( field.GetShownText( sheetPath, false ) );
1452 }
1453 }
1454
1455 return componentClasses;
1456 };
1457
1458 for( const SCH_SHEET_PATH& sheet : sheetList )
1459 {
1460 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
1461 {
1462 SCH_SHEET* sheetItem = static_cast<SCH_SHEET*>( item );
1463 std::unordered_set<wxString> sheetComponentClasses;
1464 const std::unordered_set<SCH_RULE_AREA*>& sheetRuleAreas = sheetItem->GetRuleAreaCache();
1465
1466 for( const SCH_RULE_AREA* ruleArea : sheetRuleAreas )
1467 {
1468 for( const SCH_DIRECTIVE_LABEL* label : ruleArea->GetDirectives() )
1469 {
1470 std::unordered_set<wxString> ruleAreaComponentClasses =
1471 getComponentClassFields( label->GetFields(), &sheet );
1472 sheetComponentClasses.insert( ruleAreaComponentClasses.begin(), ruleAreaComponentClasses.end() );
1473 }
1474 }
1475
1476 SCH_SHEET_PATH newPath = sheet;
1477 newPath.push_back( sheetItem );
1478 wxASSERT( !m_sheetComponentClasses.contains( newPath ) );
1479
1480 m_sheetComponentClasses[newPath] = sheetComponentClasses;
1481 }
1482 }
1483}
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:79
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
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:128
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:675
wxString GetCanonicalElectricalTypeName() const
Definition sch_pin.cpp:421
const wxString & GetShownName() const
Definition sch_pin.cpp:658
const wxString & GetShownNumber() const
Definition sch_pin.cpp:669
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.
const wxChar *const traceStackedPins
Flag to enable debug output for stacked pins handling in symbol/pin code.
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:600
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