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