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