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