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 <sch_sheet.h>
33#include <string_utils.h>
34#include <connection_graph.h>
35#include <pgm_base.h>
36#include <core/kicad_algo.h>
37#include <wx/wfstream.h>
38#include <xnode.h> // also nests: <wx/xml/xml.h>
39#include <json_common.h>
40#include <project_sch.h>
41#include <sch_rule_area.h>
42#include <trace_helpers.h>
43
44#include <map>
45#include <set>
47
48static bool sortPinsByNumber( SCH_PIN* aPin1, SCH_PIN* aPin2 );
49
50bool NETLIST_EXPORTER_XML::WriteNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
51 REPORTER& aReporter )
52{
53 // output the XML format netlist.
54
55 // declare the stream ourselves to use the buffered FILE api
56 // instead of letting wx use the syscall variant
57 wxFFileOutputStream stream( aOutFileName );
58
59 if( !stream.IsOk() )
60 return false;
61
62 wxXmlDocument xdoc;
63 xdoc.SetRoot( makeRoot( GNL_ALL | aNetlistOptions ) );
64
65 return xdoc.Save( stream, 2 /* indent bug, today was ignored by wxXml lib */ );
66}
67
68
70{
71 XNODE* xroot = node( wxT( "export" ) );
72
73 xroot->AddAttribute( wxT( "version" ), wxT( "E" ) );
74
75 if( aCtl & GNL_HEADER )
76 // add the "design" header
77 xroot->AddChild( makeDesignHeader() );
78
79 if( aCtl & GNL_SYMBOLS )
80 {
81 xroot->AddChild( makeSymbols( aCtl ) );
82
83 if( aCtl & GNL_OPT_KICAD )
84 {
85 xroot->AddChild( makeGroups() );
86 xroot->AddChild( makeVariants() );
87 }
88 }
89
90 if( aCtl & GNL_PARTS )
91 xroot->AddChild( makeLibParts() );
92
93 if( aCtl & GNL_LIBRARIES )
94 // must follow makeGenericLibParts()
95 xroot->AddChild( makeLibraries() );
96
97 if( aCtl & GNL_NETS )
98 xroot->AddChild( makeListOfNets( aCtl ) );
99
100 return xroot;
101}
102
103
105
106
108 const SCH_SHEET_PATH& aSheet,
109 const SCH_SHEET_LIST& aSheetList )
110{
111 wxString value;
112 wxString footprint;
113 wxString datasheet;
114 wxString description;
115 wxString candidate;
116 nlohmann::ordered_map<wxString, wxString> fields;
117
118 if( aSymbol->GetUnitCount() > 1 )
119 {
120 // Sadly, each unit of a symbol can have its own unique fields. This
121 // block finds the unit with the lowest number having a non blank field
122 // value and records it. Therefore user is best off setting fields
123 // into only the first unit. But this scavenger algorithm will find
124 // any non blank fields in all units and use the first non-blank field
125 // for each unique field name.
126
127 wxString ref = aSymbol->GetRef( &aSheet );
128
129 int minUnit = aSymbol->GetUnitSelection( &aSheet );
130
131 for( const SCH_SHEET_PATH& sheet : aSheetList )
132 {
133 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
134 {
135 SCH_SYMBOL* symbol2 = static_cast<SCH_SYMBOL*>( item );
136
137 wxString ref2 = symbol2->GetRef( &sheet );
138
139 if( ref2.CmpNoCase( ref ) != 0 )
140 continue;
141
142 int unit = symbol2->GetUnitSelection( &aSheet );
143
144 // The lowest unit number wins. User should only set fields in any one unit.
145
146 // Value
147 candidate = symbol2->GetValue( m_resolveTextVars, &sheet, false );
148
149 if( !candidate.IsEmpty() && ( unit < minUnit || value.IsEmpty() ) )
150 value = candidate;
151
152 // Footprint
153 candidate = symbol2->GetFootprintFieldText( m_resolveTextVars, &sheet, false );
154
155 if( !candidate.IsEmpty() && ( unit < minUnit || footprint.IsEmpty() ) )
156 footprint = candidate;
157
158 // Datasheet
159 candidate = m_resolveTextVars ? symbol2->GetField( FIELD_T::DATASHEET )->GetShownText( &sheet, false )
160 : symbol2->GetField( FIELD_T::DATASHEET )->GetText();
161
162 if( !candidate.IsEmpty() && ( unit < minUnit || datasheet.IsEmpty() ) )
163 datasheet = candidate;
164
165 // Description
166 candidate = m_resolveTextVars ? 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 ) || 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 ) || 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( &sheet ) )
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 ) || 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 const std::unique_ptr<LIB_SYMBOL>& libSym = symbol->GetLibSymbolRef();
604
605 if( libSym )
606 {
607 // A multi-unit symbol can resolve to a different lib symbol per placed unit
608 // after unit-specific edits. For instane, if you have units A B C in a multi-unit
609 // symbol, and you edit only unit B, unit B will point to new, modified lib symbol
610 // but A and C will point to the original lib symbol.
611 //
612 // Export the unit metadata from the actual unit instances's lib symbols so the PCB
613 // footprint metadata matches during backannotation, which always works per unit.
614 //
615 // However, the user isn't required to place all units, so keep a fallback default unit info.
616 const std::vector<LIB_SYMBOL::UNIT_PIN_INFO>& defaultUnitInfo = libSym->GetUnitPinInfo();
617 std::map<int, SCH_SYMBOL*> symbolByUnit;
618
619 auto addUnitSymbol =
620 [&]( SCH_SYMBOL* aUnitSymbol )
621 {
622 if( !aUnitSymbol )
623 return;
624
625 int unit = aUnitSymbol->GetUnitSelection( &sheet );
626
627 if( unit > 0 )
628 symbolByUnit.try_emplace( unit, aUnitSymbol );
629 };
630
631 addUnitSymbol( symbol );
632
633 auto extraUnitRange = extra_units.equal_range( symbol );
634
635 // Collect the other placed units that share this reference so each unit number
636 // can be resolved back to the specific SCH_SYMBOL instance on the sheet.
637 for( auto it = extraUnitRange.first; it != extraUnitRange.second; ++it )
638 addUnitSymbol( *it );
639
640 // Emit every unit slot from the default library symbol, but override that slot's
641 // metadata with the placed unit's resolved library symbol when one exists.
642 for( size_t unitIdx = 0; unitIdx < defaultUnitInfo.size(); ++unitIdx )
643 {
644 LIB_SYMBOL::UNIT_PIN_INFO unitInfo = defaultUnitInfo[unitIdx];
645 auto symbolIt = symbolByUnit.find( unitIdx + 1 );
646
647 if( symbolIt != symbolByUnit.end() )
648 {
649 const std::unique_ptr<LIB_SYMBOL>& unitLibSym = symbolIt->second->GetLibSymbolRef();
650
651 if( unitLibSym )
652 {
653 const std::vector<LIB_SYMBOL::UNIT_PIN_INFO>& unitSpecificInfo =
654 unitLibSym->GetUnitPinInfo();
655
656 if( unitIdx < unitSpecificInfo.size() )
657 unitInfo = unitSpecificInfo[unitIdx];
658 }
659 }
660
661 XNODE* xunit;
662 xunitInfo->AddChild( xunit = node( wxT( "unit" ) ) );
663 xunit->AddAttribute( wxT( "name" ), unitInfo.m_unitName );
664
665 XNODE* xpins;
666 xunit->AddChild( xpins = node( wxT( "pins" ) ) );
667
668 for( const wxString& number : unitInfo.m_pinNumbers )
669 {
670 XNODE* xpin;
671 xpins->AddChild( xpin = node( wxT( "pin" ) ) );
672 xpin->AddAttribute( wxT( "num" ), number );
673 }
674 }
675 }
676 }
677 }
678
679 m_schematic->SetCurrentSheet( currentSheet );
680
681 return xcomps;
682}
683
684
686{
687 XNODE* xcomps = node( wxT( "groups" ) );
688
690 // Do not clear m_libParts here: it is populated in makeSymbols() and used later by
691 // makeLibParts() to emit the libparts section for CvPcb and other consumers.
692
693 SCH_SHEET_PATH currentSheet = m_schematic->CurrentSheet();
694 SCH_SHEET_LIST sheetList = m_schematic->Hierarchy();
695
696 for( const SCH_SHEET_PATH& sheet : sheetList )
697 {
698 // Change schematic CurrentSheet in each iteration to allow hierarchical
699 // resolution of text variables in sheet fields.
700 m_schematic->SetCurrentSheet( sheet );
701
702 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_GROUP_T ) )
703 {
704 SCH_GROUP* group = static_cast<SCH_GROUP*>( item );
705
706 XNODE* xgroup; // current symbol being constructed
707 xcomps->AddChild( xgroup = node( wxT( "group" ) ) );
708
709 xgroup->AddAttribute( wxT( "name" ), group->GetName() );
710 xgroup->AddAttribute( wxT( "uuid" ), group->m_Uuid.AsString() );
711 xgroup->AddAttribute( wxT( "lib_id" ), group->GetDesignBlockLibId().Format() );
712
713 XNODE* xmembers;
714 xgroup->AddChild( xmembers = node( wxT( "members" ) ) );
715
716 for( EDA_ITEM* member : group->GetItems() )
717 {
718 if( member->Type() == SCH_SYMBOL_T )
719 {
720 XNODE* xmember;
721 xmembers->AddChild( xmember = node( wxT( "member" ) ) );
722 xmember->AddAttribute( wxT( "uuid" ), member->m_Uuid.AsString() );
723 }
724 else if( member->Type() == SCH_SHEET_T )
725 {
726 SCH_SHEET_PATH subSheetPath = sheet;
727 std::vector<SCH_SHEET_PATH> descendantSheets;
728
729 subSheetPath.push_back( static_cast<SCH_SHEET*>( member ) );
730 sheetList.GetSheetsWithinPath( descendantSheets, subSheetPath );
731
732 for( const SCH_SHEET_PATH& descendantSheet : descendantSheets )
733 {
734 for( SCH_ITEM* descendantItem : descendantSheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
735 {
736 XNODE* xmember;
737 xmembers->AddChild( xmember = node( wxT( "member" ) ) );
738 xmember->AddAttribute( wxT( "uuid" ), descendantItem->m_Uuid.AsString() );
739 }
740 }
741 }
742 }
743 }
744 }
745
746 m_schematic->SetCurrentSheet( currentSheet );
747
748 return xcomps;
749}
750
751
753{
754 XNODE* xvariants = node( wxT( "variants" ) );
755
756 std::set<wxString> variantNames = m_schematic->GetVariantNames();
757
758 for( const wxString& variantName : variantNames )
759 {
760 XNODE* xvariant;
761 xvariants->AddChild( xvariant = node( wxT( "variant" ) ) );
762 xvariant->AddAttribute( wxT( "name" ), variantName );
763
764 wxString description = m_schematic->GetVariantDescription( variantName );
765
766 if( !description.IsEmpty() )
767 xvariant->AddAttribute( wxT( "description" ), description );
768 }
769
770 return xvariants;
771}
772
773
775 SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH& aSymbolSheet, const SCH_SHEET_LIST& aSheetList )
776{
777 std::vector<SCH_SHEET_PATH> symbolSheets;
778 symbolSheets.push_back( aSymbolSheet );
779
780 std::unordered_set<wxString> compClassNames = aSymbol->GetComponentClassNames( &aSymbolSheet );
781 int primaryUnit = aSymbol->GetUnitSelection( &aSymbolSheet );
782
783 if( aSymbol->GetUnitCount() > 1 )
784 {
785 const wxString ref = aSymbol->GetRef( &aSymbolSheet );
786
787 for( const SCH_SHEET_PATH& sheet : aSheetList )
788 {
789 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
790 {
791 const SCH_SYMBOL* symbol2 = static_cast<SCH_SYMBOL*>( item );
792
793 wxString ref2 = symbol2->GetRef( &sheet );
794 const int otherUnit = symbol2->GetUnitSelection( &sheet );
795
796 if( ref2.CmpNoCase( ref ) != 0 )
797 continue;
798
799 if( otherUnit == primaryUnit )
800 continue;
801
802 symbolSheets.push_back( sheet );
803
804 std::unordered_set<wxString> otherClassNames =
805 symbol2->GetComponentClassNames( &sheet );
806 compClassNames.insert( otherClassNames.begin(), otherClassNames.end() );
807 }
808 }
809 }
810
811 // Add sheet-level component classes
812 for( auto& [sheetPath, sheetCompClasses] : m_sheetComponentClasses )
813 {
814 for( SCH_SHEET_PATH& symbolSheetPath : symbolSheets )
815 {
816 if( symbolSheetPath.IsContainedWithin( sheetPath ) )
817 {
818 compClassNames.insert( sheetCompClasses.begin(), sheetCompClasses.end() );
819 }
820 }
821 }
822
823 std::vector<wxString> sortedCompClassNames( compClassNames.begin(), compClassNames.end() );
824 std::sort( sortedCompClassNames.begin(), sortedCompClassNames.end(),
825 []( const wxString& str1, const wxString& str2 )
826 {
827 return str1.Cmp( str2 ) < 0;
828 } );
829
830 return sortedCompClassNames;
831}
832
833
835{
836 SCH_SCREEN* screen;
837 XNODE* xdesign = node( wxT( "design" ) );
838 XNODE* xtitleBlock;
839 XNODE* xsheet;
840 XNODE* xcomment;
841 XNODE* xtextvar;
842 wxString sheetTxt;
843 wxFileName sourceFileName;
844
845 // the root sheet is a special sheet, call it source
846 xdesign->AddChild( node( wxT( "source" ), m_schematic->GetFileName() ) );
847
848 xdesign->AddChild( node( wxT( "date" ), GetISO8601CurrentDateTime() ) );
849
850 // which Eeschema tool
851 xdesign->AddChild( node( wxT( "tool" ), wxT( "Eeschema " ) + GetBuildVersion() ) );
852
853 const std::map<wxString, wxString>& properties = m_schematic->Project().GetTextVars();
854
855 for( const std::pair<const wxString, wxString>& prop : properties )
856 {
857 xdesign->AddChild( xtextvar = node( wxT( "textvar" ), prop.second ) );
858 xtextvar->AddAttribute( wxT( "name" ), prop.first );
859 }
860
861 /*
862 * Export the sheets information
863 */
864 unsigned sheetIndex = 1; // Human readable index
865
866 for( const SCH_SHEET_PATH& sheet : m_schematic->Hierarchy() )
867 {
868 screen = sheet.LastScreen();
869
870 xdesign->AddChild( xsheet = node( wxT( "sheet" ) ) );
871
872 // get the string representation of the sheet index number.
873 sheetTxt.Printf( wxT( "%u" ), sheetIndex++ );
874 xsheet->AddAttribute( wxT( "number" ), sheetTxt );
875 xsheet->AddAttribute( wxT( "name" ), sheet.PathHumanReadable() );
876 xsheet->AddAttribute( wxT( "tstamps" ), sheet.PathAsString() );
877
878 TITLE_BLOCK tb = screen->GetTitleBlock();
879 PROJECT* prj = &m_schematic->Project();
880
881 xsheet->AddChild( xtitleBlock = node( wxT( "title_block" ) ) );
882
883 xtitleBlock->AddChild( node( wxT( "title" ), ExpandTextVars( tb.GetTitle(), prj ) ) );
884 xtitleBlock->AddChild( node( wxT( "company" ), ExpandTextVars( tb.GetCompany(), prj ) ) );
885 xtitleBlock->AddChild( node( wxT( "rev" ), ExpandTextVars( tb.GetRevision(), prj ) ) );
886 xtitleBlock->AddChild( node( wxT( "date" ), ExpandTextVars( tb.GetDate(), prj ) ) );
887
888 // We are going to remove the fileName directories.
889 sourceFileName = wxFileName( screen->GetFileName() );
890 xtitleBlock->AddChild( node( wxT( "source" ), sourceFileName.GetFullName() ) );
891
892 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
893 xcomment->AddAttribute( wxT( "number" ), wxT( "1" ) );
894 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 0 ), prj ) );
895
896 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
897 xcomment->AddAttribute( wxT( "number" ), wxT( "2" ) );
898 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 1 ), prj ) );
899
900 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
901 xcomment->AddAttribute( wxT( "number" ), wxT( "3" ) );
902 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 2 ), prj ) );
903
904 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
905 xcomment->AddAttribute( wxT( "number" ), wxT( "4" ) );
906 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 3 ), prj ) );
907
908 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
909 xcomment->AddAttribute( wxT( "number" ), wxT( "5" ) );
910 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 4 ), prj ) );
911
912 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
913 xcomment->AddAttribute( wxT( "number" ), wxT( "6" ) );
914 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 5 ), prj ) );
915
916 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
917 xcomment->AddAttribute( wxT( "number" ), wxT( "7" ) );
918 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 6 ), prj ) );
919
920 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
921 xcomment->AddAttribute( wxT( "number" ), wxT( "8" ) );
922 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 7 ), prj ) );
923
924 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
925 xcomment->AddAttribute( wxT( "number" ), wxT( "9" ) );
926 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 8 ), prj ) );
927 }
928
929 return xdesign;
930}
931
932
934{
935 XNODE* xlibs = node( wxT( "libraries" ) ); // auto_ptr
937
938 for( std::set<wxString>::iterator it = m_libraries.begin(); it!=m_libraries.end(); ++it )
939 {
940 wxString libNickname = *it;
941 XNODE* xlibrary;
942
943 std::optional<wxString> uri = manager.GetFullURI( LIBRARY_TABLE_TYPE::SYMBOL, libNickname );
944
945 if( uri )
946 {
947 xlibs->AddChild( xlibrary = node( wxT( "library" ) ) );
948 xlibrary->AddAttribute( wxT( "logical" ), libNickname );
949 xlibrary->AddChild( node( wxT( "uri" ), *uri ) );
950 }
951
952 // @todo: add more fun stuff here
953 }
954
955 return xlibs;
956}
957
958
960{
961 XNODE* xlibparts = node( wxT( "libparts" ) ); // auto_ptr
962 std::vector<SCH_FIELD*> fieldList;
963
964 m_libraries.clear();
965
966 for( LIB_SYMBOL* lcomp : m_libParts )
967 {
968 wxString libNickname = lcomp->GetLibId().GetLibNickname();;
969
970 // The library nickname will be empty if the cache library is used.
971 if( !libNickname.IsEmpty() )
972 m_libraries.insert( libNickname ); // inserts symbol's library if unique
973
974 XNODE* xlibpart;
975 xlibparts->AddChild( xlibpart = node( wxT( "libpart" ) ) );
976 xlibpart->AddAttribute( wxT( "lib" ), libNickname );
977 xlibpart->AddAttribute( wxT( "part" ), lcomp->GetName() );
978
979 //----- show the important properties -------------------------
980 if( !lcomp->GetDescription().IsEmpty() )
981 xlibpart->AddChild( node( wxT( "description" ), lcomp->GetDescription() ) );
982
983 if( !lcomp->GetDatasheetField().GetText().IsEmpty() )
984 xlibpart->AddChild( node( wxT( "docs" ), lcomp->GetDatasheetField().GetText() ) );
985
986 // Write the footprint list
987 if( lcomp->GetFPFilters().GetCount() )
988 {
989 XNODE* xfootprints;
990 xlibpart->AddChild( xfootprints = node( wxT( "footprints" ) ) );
991
992 for( unsigned i = 0; i < lcomp->GetFPFilters().GetCount(); ++i )
993 {
994 if( !lcomp->GetFPFilters()[i].IsEmpty() )
995 xfootprints->AddChild( node( wxT( "fp" ), lcomp->GetFPFilters()[i] ) );
996 }
997 }
998
999 //----- show the fields here ----------------------------------
1000 fieldList.clear();
1001 lcomp->GetFields( fieldList );
1002
1003 XNODE* xfields;
1004 xlibpart->AddChild( xfields = node( "fields" ) );
1005
1006 for( const SCH_FIELD* field : fieldList )
1007 {
1008 XNODE* xfield;
1009 xfields->AddChild( xfield = node( wxT( "field" ), field->GetText() ) );
1010 xfield->AddAttribute( wxT( "name" ), field->GetCanonicalName() );
1011 }
1012
1013 //----- show the pins here ------------------------------------
1014 // NOTE: Expand stacked-pin notation into individual pins so downstream
1015 // tools (e.g. CvPcb) see the actual number of footprint pins.
1016 std::vector<SCH_PIN*> pinList = lcomp->GetGraphicalPins( 0, 0 );
1017
1018 /*
1019 * We must erase redundant Pins references in pinList
1020 * These redundant pins exist because some pins are found more than one time when a
1021 * symbol has multiple parts per package or has 2 representations (DeMorgan conversion).
1022 * For instance, a 74ls00 has DeMorgan conversion, with different pin shapes, and
1023 * therefore each pin appears 2 times in the list. Common pins (VCC, GND) can also be
1024 * found more than once.
1025 */
1026 sort( pinList.begin(), pinList.end(), sortPinsByNumber );
1027
1028 for( int ii = 0; ii < (int)pinList.size()-1; ii++ )
1029 {
1030 if( pinList[ii]->GetNumber() == pinList[ii+1]->GetNumber() )
1031 { // 2 pins have the same number, remove the redundant pin at index i+1
1032 pinList.erase(pinList.begin() + ii + 1);
1033 ii--;
1034 }
1035 }
1036
1037 wxLogTrace( "CVPCB_PINCOUNT",
1038 wxString::Format( "makeLibParts: lib='%s' part='%s' pinList(size)=%zu",
1039 libNickname, lcomp->GetName(), pinList.size() ) );
1040
1041 if( pinList.size() )
1042 {
1043 XNODE* pins;
1044
1045 xlibpart->AddChild( pins = node( wxT( "pins" ) ) );
1046
1047 for( unsigned i=0; i<pinList.size(); ++i )
1048 {
1049 SCH_PIN* basePin = pinList[i];
1050
1051 bool stackedValid = false;
1052 std::vector<wxString> expandedNums = basePin->GetStackedPinNumbers( &stackedValid );
1053
1054 // If stacked notation detected and valid, emit one libparts pin per expanded number.
1055 if( stackedValid && !expandedNums.empty() )
1056 {
1057 for( const wxString& num : expandedNums )
1058 {
1059 XNODE* pin;
1060 pins->AddChild( pin = node( wxT( "pin" ) ) );
1061 pin->AddAttribute( wxT( "num" ), num );
1062 pin->AddAttribute( wxT( "name" ), basePin->GetShownName() );
1063 pin->AddAttribute( wxT( "type" ), basePin->GetCanonicalElectricalTypeName() );
1064
1065 wxLogTrace( "CVPCB_PINCOUNT",
1066 wxString::Format( "makeLibParts: -> pin num='%s' name='%s' (expanded)",
1067 num, basePin->GetShownName() ) );
1068 }
1069 }
1070 else
1071 {
1072 XNODE* pin;
1073 pins->AddChild( pin = node( wxT( "pin" ) ) );
1074 pin->AddAttribute( wxT( "num" ), basePin->GetShownNumber() );
1075 pin->AddAttribute( wxT( "name" ), basePin->GetShownName() );
1076 pin->AddAttribute( wxT( "type" ), basePin->GetCanonicalElectricalTypeName() );
1077
1078 wxLogTrace( "CVPCB_PINCOUNT",
1079 wxString::Format( "makeLibParts: -> pin num='%s' name='%s'",
1080 basePin->GetShownNumber(),
1081 basePin->GetShownName() ) );
1082 }
1083
1084 // caution: construction work site here, drive slowly
1085 }
1086 }
1087 }
1088
1089 return xlibparts;
1090}
1091
1092
1094{
1095 wxString netCodeTxt;
1096 XNODE* xnets = node( wxT( "nets" ) ); // auto_ptr if exceptions ever get used.
1097 XNODE* xnet = nullptr;
1098
1099 /* output:
1100 <net code="123" name="/cfcard.sch/WAIT#" class="signal">
1101 <node ref="R23" pin="1"/>
1102 <node ref="U18" pin="12"/>
1103 </net>
1104 */
1105
1106 struct NET_NODE
1107 {
1108 NET_NODE( SCH_PIN* aPin, const SCH_SHEET_PATH& aSheet ) :
1109 m_Pin( aPin ),
1110 m_Sheet( aSheet )
1111 {}
1112
1113 SCH_PIN* m_Pin;
1114 SCH_SHEET_PATH m_Sheet;
1115 };
1116
1117 struct NET_RECORD
1118 {
1119 NET_RECORD( const wxString& aName ) :
1120 m_Name( aName ),
1121 m_HasNoConnect( false )
1122 {};
1123
1124 wxString m_Name;
1125 wxString m_Class;
1126 bool m_HasNoConnect;
1127 std::vector<NET_NODE> m_Nodes;
1128 };
1129
1130 std::vector<NET_RECORD*> nets;
1131
1132 for( const auto& [ key, subgraphs ] : m_schematic->ConnectionGraph()->GetNetMap() )
1133 {
1134 wxString net_name = key.Name;
1135 NET_RECORD* net_record = nullptr;
1136
1137 if( !( aCtl & GNL_OPT_KICAD ) )
1138 net_name = UnescapeString( net_name );
1139
1140 if( subgraphs.empty() )
1141 continue;
1142
1143 nets.emplace_back( new NET_RECORD( net_name ) );
1144 net_record = nets.back();
1145
1146 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
1147 {
1148 bool nc = subgraph->GetNoConnect() && subgraph->GetNoConnect()->Type() == SCH_NO_CONNECT_T;
1149 const SCH_SHEET_PATH& sheet = subgraph->GetSheet();
1150
1151 if( net_record->m_Class.IsEmpty() && subgraph->GetDriver() )
1152 {
1153 if( subgraph->GetDriver()->GetEffectiveNetClass() )
1154 {
1155 net_record->m_Class = subgraph->GetDriver()->GetEffectiveNetClass()->GetName();
1156 net_record->m_Class = UnescapeString( net_record->m_Class );
1157 }
1158 }
1159
1160 if( nc )
1161 net_record->m_HasNoConnect = true;
1162
1163 for( SCH_ITEM* item : subgraph->GetItems() )
1164 {
1165 if( item->Type() == SCH_PIN_T )
1166 {
1167 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
1168 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
1169 bool forBOM = aCtl & GNL_OPT_BOM;
1170 bool forBoard = aCtl & GNL_OPT_KICAD;
1171
1172 if( !symbol )
1173 continue;
1174
1175 if( forBOM && ( sheet.GetExcludedFromBOM() || symbol->ResolveExcludedFromBOM() ) )
1176 continue;
1177
1178 if( forBoard && ( sheet.GetExcludedFromBoard() || symbol->ResolveExcludedFromBoard() ) )
1179 continue;
1180
1181 net_record->m_Nodes.emplace_back( pin, sheet );
1182 }
1183 }
1184 }
1185 }
1186
1187 // Netlist ordering: Net name, then ref des, then pin name
1188 std::sort( nets.begin(), nets.end(),
1189 []( const NET_RECORD* a, const NET_RECORD*b )
1190 {
1191 return StrNumCmp( a->m_Name, b->m_Name ) < 0;
1192 } );
1193
1194 for( int i = 0; i < (int) nets.size(); ++i )
1195 {
1196 NET_RECORD* net_record = nets[i];
1197 bool added = false;
1198 XNODE* xnode;
1199
1200 // Netlist ordering: Net name, then ref des, then pin name
1201 std::sort( net_record->m_Nodes.begin(), net_record->m_Nodes.end(),
1202 []( const NET_NODE& a, const NET_NODE& b )
1203 {
1204 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
1205 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
1206
1207 if( refA == refB )
1208 return a.m_Pin->GetShownNumber() < b.m_Pin->GetShownNumber();
1209
1210 return refA < refB;
1211 } );
1212
1213 // Some duplicates can exist, for example on multi-unit parts with duplicated pins across
1214 // units. If the user connects the pins on each unit, they will appear on separate
1215 // subgraphs. Remove those here:
1216 alg::remove_duplicates( net_record->m_Nodes,
1217 []( const NET_NODE& a, const NET_NODE& b )
1218 {
1219 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
1220 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
1221
1222 return refA == refB && a.m_Pin->GetShownNumber() == b.m_Pin->GetShownNumber();
1223 } );
1224
1225 // Determine if all pins in the net are stacked (nets with only one pin are implicitly
1226 // taken to be stacked)
1227 bool allNetPinsStacked = true;
1228
1229 if( net_record->m_Nodes.size() > 1 )
1230 {
1231 SCH_PIN* firstPin = net_record->m_Nodes.begin()->m_Pin;
1232 allNetPinsStacked =
1233 std::all_of( net_record->m_Nodes.begin() + 1, net_record->m_Nodes.end(),
1234 [=]( auto& node )
1235 {
1236 return firstPin->GetParent() == node.m_Pin->GetParent()
1237 && firstPin->GetPosition() == node.m_Pin->GetPosition()
1238 && firstPin->GetName() == node.m_Pin->GetName();
1239 } );
1240 }
1241
1242 for( const NET_NODE& netNode : net_record->m_Nodes )
1243 {
1244 wxString refText = netNode.m_Pin->GetParentSymbol()->GetRef( &netNode.m_Sheet );
1245
1246 // Skip power symbols and virtual symbols
1247 if( refText[0] == wxChar( '#' ) )
1248 continue;
1249
1250 if( !added )
1251 {
1252 netCodeTxt.Printf( wxT( "%d" ), i + 1 );
1253
1254 xnets->AddChild( xnet = node( wxT( "net" ) ) );
1255 xnet->AddAttribute( wxT( "code" ), netCodeTxt );
1256 xnet->AddAttribute( wxT( "name" ), net_record->m_Name );
1257 xnet->AddAttribute( wxT( "class" ), net_record->m_Class );
1258
1259 added = true;
1260 }
1261
1262 std::vector<wxString> nums = netNode.m_Pin->GetStackedPinNumbers();
1263 wxString baseName = netNode.m_Pin->GetShownName();
1264 wxString pinType = netNode.m_Pin->GetCanonicalElectricalTypeName();
1265
1266 wxLogTrace( traceStackedPins,
1267 wxString::Format( "XML: net='%s' ref='%s' base='%s' shownNum='%s' expand=%zu",
1268 net_record->m_Name, refText, baseName,
1269 netNode.m_Pin->GetShownNumber(), nums.size() ) );
1270
1271 for( const wxString& num : nums )
1272 {
1273 xnet->AddChild( xnode = node( wxT( "node" ) ) );
1274 xnode->AddAttribute( wxT( "ref" ), refText );
1275 xnode->AddAttribute( wxT( "pin" ), num );
1276
1277 wxString fullName = baseName.IsEmpty() ? num : baseName + wxT( "_" ) + num;
1278
1279 if( !baseName.IsEmpty() || nums.size() > 1 )
1280 xnode->AddAttribute( wxT( "pinfunction" ), fullName );
1281
1282 wxString typeAttr = pinType;
1283
1284 if( net_record->m_HasNoConnect
1285 && ( net_record->m_Nodes.size() == 1 || allNetPinsStacked ) )
1286 {
1287 typeAttr += wxT( "+no_connect" );
1288 wxLogTrace( traceStackedPins,
1289 wxString::Format( "XML: marking node ref='%s' pin='%s' as no_connect",
1290 refText, num ) );
1291 }
1292
1293 xnode->AddAttribute( wxT( "pintype" ), typeAttr );
1294 }
1295 }
1296 }
1297
1298 for( NET_RECORD* record : nets )
1299 delete record;
1300
1301 return xnets;
1302}
1303
1304
1305XNODE* NETLIST_EXPORTER_XML::node( const wxString& aName,
1306 const wxString& aTextualContent /* = wxEmptyString*/ )
1307{
1308 XNODE* n = new XNODE( wxXML_ELEMENT_NODE, aName );
1309
1310 if( aTextualContent.Len() > 0 ) // excludes wxEmptyString, the parameter's default value
1311 n->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) );
1312
1313 return n;
1314}
1315
1316
1317static bool sortPinsByNumber( SCH_PIN* aPin1, SCH_PIN* aPin2 )
1318{
1319 // return "lhs < rhs"
1320 return StrNumCmp( aPin1->GetShownNumber(), aPin2->GetShownNumber(), true ) < 0;
1321}
1323{
1325
1326 SCH_SHEET_LIST sheetList = m_schematic->Hierarchy();
1327
1328 auto getComponentClassFields = [&]( const std::vector<SCH_FIELD>& fields, const SCH_SHEET_PATH* sheetPath )
1329 {
1330 std::unordered_set<wxString> componentClasses;
1331
1332 for( const SCH_FIELD& field : fields )
1333 {
1334 if( field.GetCanonicalName() == wxT( "Component Class" ) )
1335 {
1336 if( field.GetShownText( sheetPath, false ) != wxEmptyString )
1337 componentClasses.insert( field.GetShownText( sheetPath, false ) );
1338 }
1339 }
1340
1341 return componentClasses;
1342 };
1343
1344 for( const SCH_SHEET_PATH& sheet : sheetList )
1345 {
1346 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SHEET_T ) )
1347 {
1348 SCH_SHEET* sheetItem = static_cast<SCH_SHEET*>( item );
1349 std::unordered_set<wxString> sheetComponentClasses;
1350 const std::unordered_set<SCH_RULE_AREA*>& sheetRuleAreas = sheetItem->GetRuleAreaCache();
1351
1352 for( const SCH_RULE_AREA* ruleArea : sheetRuleAreas )
1353 {
1354 for( const SCH_DIRECTIVE_LABEL* label : ruleArea->GetDirectives() )
1355 {
1356 std::unordered_set<wxString> ruleAreaComponentClasses =
1357 getComponentClassFields( label->GetFields(), &sheet );
1358 sheetComponentClasses.insert( ruleAreaComponentClasses.begin(), ruleAreaComponentClasses.end() );
1359 }
1360 }
1361
1362 SCH_SHEET_PATH newPath = sheet;
1363 newPath.push_back( sheetItem );
1364 wxASSERT( !m_sheetComponentClasses.contains( newPath ) );
1365
1366 m_sheetComponentClasses[newPath] = sheetComponentClasses;
1367 }
1368 }
1369}
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:100
const KIID m_Uuid
Definition eda_item.h:528
wxString AsString() const
Definition kiid.cpp:244
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false)
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
const wxString GetUniStringLibItemName() const
Get strings for display messages in dialogs.
Definition lib_id.h:112
const wxString GetUniStringLibNickname() const
Definition lib_id.h:88
Define a library symbol object.
Definition lib_symbol.h:83
SCHEMATIC * m_schematic
The schematic we're generating a netlist for.
SCH_SYMBOL * findNextSymbol(EDA_ITEM *aItem, const SCH_SHEET_PATH &aSheetPath)
Check if the given symbol should be processed for netlisting.
std::set< LIB_SYMBOL *, LIB_SYMBOL_LESS_THAN > m_libParts
unique library symbols used. LIB_SYMBOL items are sorted by names
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
std::map< SCH_SHEET_PATH, std::unordered_set< wxString > > m_sheetComponentClasses
Map of all sheets to component classes covering the whole sheet.
XNODE * makeDesignHeader()
Fill out a project "design" header into an XML node.
XNODE * makeLibraries()
Fill out an XML node with a list of used libraries and returns it.
bool WriteNetlist(const wxString &aOutFileName, unsigned aNetlistOptions, REPORTER &aReporter) override
Write generic netlist to aOutFileName.
XNODE * node(const wxString &aName, const wxString &aTextualContent=wxEmptyString)
A convenience function that creates a new XNODE with an optional textual child.
XNODE * makeListOfNets(unsigned aCtl)
Fill out an XML node with a list of nets and returns it.
void addSymbolFields(XNODE *aNode, SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aSheet, const SCH_SHEET_LIST &aSheetList)
Holder for multi-unit symbol fields.
std::vector< wxString > getComponentClassNamesForAllSymbolUnits(SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aSymbolSheet, const SCH_SHEET_LIST &aSheetList)
Finds all component class names attached to any sub-unit of a given symbol.
XNODE * makeSymbols(unsigned aCtl)
XNODE * makeRoot(unsigned aCtl=GNL_ALL)
Build the entire document tree for the generic export.
std::set< wxString > m_libraries
XNODE * makeLibParts()
Fill out an XML node with the unique library parts and returns it.
virtual LIBRARY_MANAGER & GetLibraryManager() const
Definition pgm_base.h:132
Container for project specific data.
Definition project.h:66
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:75
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:126
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0, const wxString &aVariantName=wxEmptyString) const
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:52
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
bool ResolveExcludedFromPosFiles(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:350
const std::unordered_set< SCH_RULE_AREA * > & GetRuleAreaCache() const
Get the cache of rule areas enclosing this item.
Definition sch_item.h:687
bool ResolveExcludedFromBOM(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:318
wxString ResolveText(const wxString &aText, const SCH_SHEET_PATH *aPath, int aDepth=0) const
Definition sch_item.cpp:381
bool ResolveExcludedFromBoard(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:334
bool ResolveDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const
Definition sch_item.cpp:366
std::vector< wxString > GetStackedPinNumbers(bool *aValid=nullptr) const
Definition sch_pin.cpp:679
wxString GetCanonicalElectricalTypeName() const
Definition sch_pin.cpp:425
const wxString & GetShownName() const
Definition sch_pin.cpp:662
const wxString & GetShownNumber() const
Definition sch_pin.cpp:673
const wxString & GetFileName() const
Definition sch_screen.h:154
TITLE_BLOCK & GetTitleBlock()
Definition sch_screen.h:165
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void GetSheetsWithinPath(std::vector< SCH_SHEET_PATH > &aSheets, const SCH_SHEET_PATH &aSheetPath) const
Add a SCH_SHEET_PATH object to aSheets for each sheet in the list that are contained within aSheetPat...
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
bool GetExcludedFromBOM() const
const SCH_SHEET * GetSheet(unsigned aIndex) const
bool GetExcludedFromBoard() const
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
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:63
The common library.
const wxChar *const traceStackedPins
Flag to enable debug output for stacked pins handling in symbol/pin code.
void remove_duplicates(_Container &__c)
Deletes all duplicate values from __c.
Definition kicad_algo.h:161
static bool sortPinsByNumber(SCH_PIN *aPin1, SCH_PIN *aPin2)
#define GNL_ALL
@ GNL_LIBRARIES
@ GNL_OPT_KICAD
@ GNL_SYMBOLS
@ GNL_OPT_BOM
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
Class to handle a set of SCH_ITEMs.
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString UnescapeString(const wxString &aSource)
wxString GetISO8601CurrentDateTime()
std::vector< wxString > m_pinNumbers
Definition lib_symbol.h:603
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:174
@ SCH_NO_CONNECT_T
Definition typeinfo.h:161
@ SCH_SYMBOL_T
Definition typeinfo.h:173
@ SCH_SHEET_T
Definition typeinfo.h:176
@ SCH_PIN_T
Definition typeinfo.h:154