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 <symbol_library.h>
32#include <string_utils.h>
33#include <connection_graph.h>
34#include <core/kicad_algo.h>
35#include <wx/wfstream.h>
36#include <xnode.h> // also nests: <wx/xml/xml.h>
37#include <nlohmann/json.hpp>
38#include <project_sch.h>
39
40#include <symbol_lib_table.h>
41
42#include <set>
43
44static bool sortPinsByNumber( SCH_PIN* aPin1, SCH_PIN* aPin2 );
45
46bool NETLIST_EXPORTER_XML::WriteNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
47 REPORTER& aReporter )
48{
49 // output the XML format netlist.
50
51 // declare the stream ourselves to use the buffered FILE api
52 // instead of letting wx use the syscall variant
53 wxFFileOutputStream stream( aOutFileName );
54
55 if( !stream.IsOk() )
56 return false;
57
58 wxXmlDocument xdoc;
59 xdoc.SetRoot( makeRoot( GNL_ALL | aNetlistOptions ) );
60
61 return xdoc.Save( stream, 2 /* indent bug, today was ignored by wxXml lib */ );
62}
63
64
66{
67 XNODE* xroot = node( wxT( "export" ) );
68
69 xroot->AddAttribute( wxT( "version" ), wxT( "E" ) );
70
71 if( aCtl & GNL_HEADER )
72 // add the "design" header
73 xroot->AddChild( makeDesignHeader() );
74
75 if( aCtl & GNL_SYMBOLS )
76 xroot->AddChild( makeSymbols( aCtl ) );
77
78 if( aCtl & GNL_PARTS )
79 xroot->AddChild( makeLibParts() );
80
81 if( aCtl & GNL_LIBRARIES )
82 // must follow makeGenericLibParts()
83 xroot->AddChild( makeLibraries() );
84
85 if( aCtl & GNL_NETS )
86 xroot->AddChild( makeListOfNets( aCtl ) );
87
88 return xroot;
89}
90
91
93
94
96 const SCH_SHEET_PATH& aSheet,
97 const SCH_SHEET_LIST& aSheetList )
98{
99 wxString value;
100 wxString footprint;
101 wxString datasheet;
102 wxString description;
103 wxString candidate;
104 nlohmann::ordered_map<wxString, wxString> fields;
105
106 if( aSymbol->GetUnitCount() > 1 )
107 {
108 // Sadly, each unit of a symbol can have its own unique fields. This
109 // block finds the unit with the lowest number having a non blank field
110 // value and records it. Therefore user is best off setting fields
111 // into only the first unit. But this scavenger algorithm will find
112 // any non blank fields in all units and use the first non-blank field
113 // for each unique field name.
114
115 wxString ref = aSymbol->GetRef( &aSheet );
116
117 int minUnit = aSymbol->GetUnitSelection( &aSheet );
118
119 for( const SCH_SHEET_PATH& sheet : aSheetList )
120 {
121 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
122 {
123 SCH_SYMBOL* symbol2 = static_cast<SCH_SYMBOL*>( item );
124
125 wxString ref2 = symbol2->GetRef( &sheet );
126
127 if( ref2.CmpNoCase( ref ) != 0 )
128 continue;
129
130 int unit = symbol2->GetUnitSelection( &aSheet );
131
132 // The lowest unit number wins. User should only set fields in any one unit.
133
134 // Value
135 candidate = symbol2->GetValue( m_resolveTextVars, &sheet, false );
136
137 if( !candidate.IsEmpty() && ( unit < minUnit || value.IsEmpty() ) )
138 value = candidate;
139
140 // Footprint
141 candidate = symbol2->GetFootprintFieldText( m_resolveTextVars, &sheet, false );
142
143 if( !candidate.IsEmpty() && ( unit < minUnit || footprint.IsEmpty() ) )
144 footprint = candidate;
145
146 // Datasheet
147 candidate = m_resolveTextVars
148 ? symbol2->GetField( DATASHEET_FIELD )->GetShownText( &sheet, false )
149 : symbol2->GetField( DATASHEET_FIELD )->GetText();
150
151 if( !candidate.IsEmpty() && ( unit < minUnit || datasheet.IsEmpty() ) )
152 datasheet = candidate;
153
154 // Description
155 candidate = m_resolveTextVars
156 ? symbol2->GetField( DESCRIPTION_FIELD )->GetShownText( &sheet, false )
157 : symbol2->GetField( DESCRIPTION_FIELD )->GetText();
158
159 if( !candidate.IsEmpty() && ( unit < minUnit || description.IsEmpty() ) )
160 description = candidate;
161
162 // All non-mandatory fields
163 for( SCH_FIELD& field : symbol2->GetFields() )
164 {
165 if( field.IsMandatory() )
166 continue;
167
168 if( unit < minUnit || fields.count( field.GetName() ) == 0 )
169 {
171 fields[field.GetName()] = field.GetShownText( &aSheet, false );
172 else
173 fields[field.GetName()] = field.GetText();
174 }
175 }
176
177 minUnit = std::min( unit, minUnit );
178 }
179 }
180 }
181 else
182 {
183 value = aSymbol->GetValue( m_resolveTextVars, &aSheet, false );
184 footprint = aSymbol->GetFootprintFieldText( m_resolveTextVars, &aSheet, false );
185
186 SCH_FIELD* datasheetField = aSymbol->GetField( DATASHEET_FIELD );
187 SCH_FIELD* descriptionField = aSymbol->GetField( DESCRIPTION_FIELD );
188
189 // Datasheet
191 datasheet = datasheetField->GetShownText( &aSheet, false );
192 else
193 datasheet = datasheetField->GetText();
194
195 // Description
197 description = descriptionField->GetShownText( &aSheet, false );
198 else
199 description = descriptionField->GetText();
200
201 for( SCH_FIELD& field : aSymbol->GetFields() )
202 {
203 if( field.IsMandatory() )
204 continue;
205
207 fields[field.GetName()] = field.GetShownText( &aSheet, false );
208 else
209 fields[field.GetName()] = field.GetText();
210 }
211 }
212
213 fields[GetCanonicalFieldName( FOOTPRINT_FIELD )] = footprint;
215 fields[GetCanonicalFieldName( DESCRIPTION_FIELD )] = description;
216
217 // Do not output field values blank in netlist:
218 if( value.size() )
219 aNode->AddChild( node( wxT( "value" ), UnescapeString( value ) ) );
220 else // value field always written in netlist
221 aNode->AddChild( node( wxT( "value" ), wxT( "~" ) ) );
222
223 if( footprint.size() )
224 aNode->AddChild( node( wxT( "footprint" ), UnescapeString( footprint ) ) );
225
226 if( datasheet.size() )
227 aNode->AddChild( node( wxT( "datasheet" ), UnescapeString( datasheet ) ) );
228
229 if( description.size() )
230 aNode->AddChild( node( wxT( "description" ), UnescapeString( description ) ) );
231
232 XNODE* xfields;
233 aNode->AddChild( xfields = node( wxT( "fields" ) ) );
234
235 for( const auto& [ fieldName, fieldValue ] : fields )
236 {
237 XNODE* xfield = node( wxT( "field" ), UnescapeString( fieldValue ) );
238 xfield->AddAttribute( wxT( "name" ), UnescapeString( fieldName ) );
239 xfields->AddChild( xfield );
240 }
241}
242
243
245{
246 XNODE* xcomps = node( wxT( "components" ) );
247
249 m_libParts.clear();
250
251 SCH_SHEET_PATH currentSheet = m_schematic->CurrentSheet();
252 SCH_SHEET_LIST sheetList = m_schematic->Hierarchy();
253
254 // Output is xml, so there is no reason to remove spaces from the field values.
255 // And XML element names need not be translated to various languages.
256
257 for( const SCH_SHEET_PATH& sheet : sheetList )
258 {
259 // Change schematic CurrentSheet in each iteration to allow hierarchical
260 // resolution of text variables in sheet fields.
262
263 auto cmp =
264 [sheet]( SCH_SYMBOL* a, SCH_SYMBOL* b )
265 {
266 return ( StrNumCmp( a->GetRef( &sheet, false ),
267 b->GetRef( &sheet, false ), true ) < 0 );
268 };
269
270 std::set<SCH_SYMBOL*, decltype( cmp )> ordered_symbols( cmp );
271 std::multiset<SCH_SYMBOL*, decltype( cmp )> extra_units( cmp );
272
273 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
274 {
275 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
276 auto test = ordered_symbols.insert( symbol );
277
278 if( !test.second )
279 {
280 if( ( *( test.first ) )->m_Uuid > symbol->m_Uuid )
281 {
282 extra_units.insert( *( test.first ) );
283 ordered_symbols.erase( test.first );
284 ordered_symbols.insert( symbol );
285 }
286 else
287 {
288 extra_units.insert( symbol );
289 }
290 }
291 }
292
293 for( EDA_ITEM* item : ordered_symbols )
294 {
295 SCH_SYMBOL* symbol = findNextSymbol( item, sheet );
296 bool forBOM = aCtl & GNL_OPT_BOM;
297 bool forBoard = aCtl & GNL_OPT_KICAD;
298
299 if( !symbol )
300 continue;
301
302 if( forBOM && ( sheet.GetExcludedFromBOM() || symbol->GetExcludedFromBOM() ) )
303 continue;
304
305 if( forBoard && ( sheet.GetExcludedFromBoard() || symbol->GetExcludedFromBoard() ) )
306 continue;
307
308 // Output the symbol's elements in order of expected access frequency. This may
309 // not always look best, but it will allow faster execution under XSL processing
310 // systems which do sequential searching within an element.
311
312 XNODE* xcomp; // current symbol being constructed
313 xcomps->AddChild( xcomp = node( wxT( "comp" ) ) );
314
315 xcomp->AddAttribute( wxT( "ref" ), symbol->GetRef( &sheet ) );
316 addSymbolFields( xcomp, symbol, sheet, sheetList );
317
318 XNODE* xlibsource;
319 xcomp->AddChild( xlibsource = node( wxT( "libsource" ) ) );
320
321 // "logical" library name, which is in anticipation of a better search algorithm
322 // for parts based on "logical_lib.part" and where logical_lib is merely the library
323 // name minus path and extension.
324 wxString libName;
325 wxString partName;
326
327 if( symbol->UseLibIdLookup() )
328 {
329 libName = symbol->GetLibId().GetUniStringLibNickname();
330 partName = symbol->GetLibId().GetUniStringLibItemName();
331 }
332 else
333 {
334 partName = symbol->GetSchSymbolLibraryName();
335 }
336
337 xlibsource->AddAttribute( wxT( "lib" ), libName );
338
339 // We only want the symbol name, not the full LIB_ID.
340 xlibsource->AddAttribute( wxT( "part" ), partName );
341
342 xlibsource->AddAttribute( wxT( "description" ), symbol->GetDescription() );
343
344 /* Add the symbol properties. */
345 XNODE* xproperty;
346
347 std::vector<SCH_FIELD>& fields = symbol->GetFields();
348
349 for( size_t jj = MANDATORY_FIELDS; jj < fields.size(); ++jj )
350 {
351 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
352 xproperty->AddAttribute( wxT( "name" ), fields[jj].GetCanonicalName() );
353
355 xproperty->AddAttribute( wxT( "value" ), fields[jj].GetShownText( &sheet, false ) );
356 else
357 xproperty->AddAttribute( wxT( "value" ), fields[jj].GetText() );
358 }
359
360 for( const SCH_FIELD& sheetField : sheet.Last()->GetFields() )
361 {
362 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
363 xproperty->AddAttribute( wxT( "name" ), sheetField.GetCanonicalName() );
364
366 // do not allow GetShownText() to add any prefix useful only when displaying
367 // the field on screen
368 xproperty->AddAttribute( wxT( "value" ), sheetField.GetShownText( &sheet, false ) );
369 else
370 xproperty->AddAttribute( wxT( "value" ), sheetField.GetText() );
371 }
372
373 if( symbol->GetExcludedFromBOM() || sheet.GetExcludedFromBOM() )
374 {
375 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
376 xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_bom" ) );
377 }
378
379 if( symbol->GetExcludedFromBoard() || sheet.GetExcludedFromBoard() )
380 {
381 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
382 xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_board" ) );
383 }
384
385 if( symbol->GetDNP() || sheet.GetDNP() )
386 {
387 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
388 xproperty->AddAttribute( wxT( "name" ), wxT( "dnp" ) );
389 }
390
391 if( const std::unique_ptr<LIB_SYMBOL>& part = symbol->GetLibSymbolRef() )
392 {
393 if( part->GetKeyWords().size() )
394 {
395 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
396 xproperty->AddAttribute( wxT( "name" ), wxT( "ki_keywords" ) );
397 xproperty->AddAttribute( wxT( "value" ), part->GetKeyWords() );
398 }
399
400 if( !part->GetFPFilters().IsEmpty() )
401 {
402 wxString filters;
403
404 for( const wxString& filter : part->GetFPFilters() )
405 filters += ' ' + filter;
406
407 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
408 xproperty->AddAttribute( wxT( "name" ), wxT( "ki_fp_filters" ) );
409 xproperty->AddAttribute( wxT( "value" ), filters.Trim( false ) );
410 }
411 }
412
413 XNODE* xsheetpath;
414 xcomp->AddChild( xsheetpath = node( wxT( "sheetpath" ) ) );
415
416 xsheetpath->AddAttribute( wxT( "names" ), sheet.PathHumanReadable() );
417 xsheetpath->AddAttribute( wxT( "tstamps" ), sheet.PathAsString() );
418
419 // Node for component class
420 std::vector<wxString> compClassNames =
421 getComponentClassNamesForAllSymbolUnits( symbol, sheet, sheetList );
422
423 if( compClassNames.size() > 0 )
424 {
425 XNODE* xcompclasslist;
426 xcomp->AddChild( xcompclasslist = node( wxT( "component_classes" ) ) );
427
428 for( const wxString& compClass : compClassNames )
429 {
430 xcompclasslist->AddChild( node( wxT( "class" ), UnescapeString( compClass ) ) );
431 }
432 }
433
434 XNODE* xunits; // Node for extra units
435 xcomp->AddChild( xunits = node( wxT( "tstamps" ) ) );
436
437 auto range = extra_units.equal_range( symbol );
438 wxString uuid;
439
440 // Output a series of children with all UUIDs associated with the REFDES
441 for( auto it = range.first; it != range.second; ++it )
442 {
443 uuid = ( *it )->m_Uuid.AsString();
444
445 // Add a space between UUIDs, if not in KICAD mode (i.e.
446 // using wxXmlDocument::Save()). KICAD MODE has its own XNODE::Format function.
447 if( !( aCtl & GNL_OPT_KICAD ) ) // i.e. for .xml format
448 uuid += ' ';
449
450 xunits->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, uuid ) );
451 }
452
453 // Output the primary UUID
454 uuid = symbol->m_Uuid.AsString();
455 xunits->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, uuid ) );
456 }
457 }
458
459 m_schematic->SetCurrentSheet( currentSheet );
460
461 return xcomps;
462}
463
464
466 SCH_SYMBOL* aSymbol, const SCH_SHEET_PATH& aSymbolSheet, const SCH_SHEET_LIST& aSheetList )
467{
468 std::unordered_set<wxString> compClassNames = aSymbol->GetComponentClassNames( &aSymbolSheet );
469 int primaryUnit = aSymbol->GetUnitSelection( &aSymbolSheet );
470
471 if( aSymbol->GetUnitCount() > 1 )
472 {
473 wxString ref = aSymbol->GetRef( &aSymbolSheet );
474
475 for( const SCH_SHEET_PATH& sheet : aSheetList )
476 {
477 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
478 {
479 SCH_SYMBOL* symbol2 = static_cast<SCH_SYMBOL*>( item );
480
481 wxString ref2 = symbol2->GetRef( &sheet );
482 int otherUnit = symbol2->GetUnitSelection( &sheet );
483
484 if( ref2.CmpNoCase( ref ) != 0 )
485 continue;
486
487 if( otherUnit == primaryUnit )
488 continue;
489
490 std::unordered_set<wxString> otherClassNames =
491 symbol2->GetComponentClassNames( &sheet );
492 compClassNames.insert( otherClassNames.begin(), otherClassNames.end() );
493 }
494 }
495 }
496
497 std::vector<wxString> sortedCompClassNames( compClassNames.begin(), compClassNames.end() );
498 std::sort( sortedCompClassNames.begin(), sortedCompClassNames.end(),
499 []( const wxString& str1, const wxString& str2 )
500 {
501 return str1.Cmp( str2 ) < 0;
502 } );
503
504 return sortedCompClassNames;
505}
506
507
509{
510 SCH_SCREEN* screen;
511 XNODE* xdesign = node( wxT( "design" ) );
512 XNODE* xtitleBlock;
513 XNODE* xsheet;
514 XNODE* xcomment;
515 XNODE* xtextvar;
516 wxString sheetTxt;
517 wxFileName sourceFileName;
518
519 // the root sheet is a special sheet, call it source
520 xdesign->AddChild( node( wxT( "source" ), m_schematic->GetFileName() ) );
521
522 xdesign->AddChild( node( wxT( "date" ), GetISO8601CurrentDateTime() ) );
523
524 // which Eeschema tool
525 xdesign->AddChild( node( wxT( "tool" ), wxT( "Eeschema " ) + GetBuildVersion() ) );
526
527 const std::map<wxString, wxString>& properties = m_schematic->Prj().GetTextVars();
528
529 for( const std::pair<const wxString, wxString>& prop : properties )
530 {
531 xdesign->AddChild( xtextvar = node( wxT( "textvar" ), prop.second ) );
532 xtextvar->AddAttribute( wxT( "name" ), prop.first );
533 }
534
535 /*
536 * Export the sheets information
537 */
538 unsigned sheetIndex = 1; // Human readable index
539
540 for( const SCH_SHEET_PATH& sheet : m_schematic->Hierarchy() )
541 {
542 screen = sheet.LastScreen();
543
544 xdesign->AddChild( xsheet = node( wxT( "sheet" ) ) );
545
546 // get the string representation of the sheet index number.
547 sheetTxt.Printf( wxT( "%u" ), sheetIndex++ );
548 xsheet->AddAttribute( wxT( "number" ), sheetTxt );
549 xsheet->AddAttribute( wxT( "name" ), sheet.PathHumanReadable() );
550 xsheet->AddAttribute( wxT( "tstamps" ), sheet.PathAsString() );
551
552 TITLE_BLOCK tb = screen->GetTitleBlock();
553 PROJECT* prj = &m_schematic->Prj();
554
555 xsheet->AddChild( xtitleBlock = node( wxT( "title_block" ) ) );
556
557 xtitleBlock->AddChild( node( wxT( "title" ), ExpandTextVars( tb.GetTitle(), prj ) ) );
558 xtitleBlock->AddChild( node( wxT( "company" ), ExpandTextVars( tb.GetCompany(), prj ) ) );
559 xtitleBlock->AddChild( node( wxT( "rev" ), ExpandTextVars( tb.GetRevision(), prj ) ) );
560 xtitleBlock->AddChild( node( wxT( "date" ), ExpandTextVars( tb.GetDate(), prj ) ) );
561
562 // We are going to remove the fileName directories.
563 sourceFileName = wxFileName( screen->GetFileName() );
564 xtitleBlock->AddChild( node( wxT( "source" ), sourceFileName.GetFullName() ) );
565
566 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
567 xcomment->AddAttribute( wxT( "number" ), wxT( "1" ) );
568 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 0 ), prj ) );
569
570 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
571 xcomment->AddAttribute( wxT( "number" ), wxT( "2" ) );
572 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 1 ), prj ) );
573
574 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
575 xcomment->AddAttribute( wxT( "number" ), wxT( "3" ) );
576 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 2 ), prj ) );
577
578 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
579 xcomment->AddAttribute( wxT( "number" ), wxT( "4" ) );
580 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 3 ), prj ) );
581
582 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
583 xcomment->AddAttribute( wxT( "number" ), wxT( "5" ) );
584 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 4 ), prj ) );
585
586 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
587 xcomment->AddAttribute( wxT( "number" ), wxT( "6" ) );
588 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 5 ), prj ) );
589
590 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
591 xcomment->AddAttribute( wxT( "number" ), wxT( "7" ) );
592 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 6 ), prj ) );
593
594 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
595 xcomment->AddAttribute( wxT( "number" ), wxT( "8" ) );
596 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 7 ), prj ) );
597
598 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
599 xcomment->AddAttribute( wxT( "number" ), wxT( "9" ) );
600 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 8 ), prj ) );
601 }
602
603 return xdesign;
604}
605
606
608{
609 XNODE* xlibs = node( wxT( "libraries" ) ); // auto_ptr
611
612 for( std::set<wxString>::iterator it = m_libraries.begin(); it!=m_libraries.end(); ++it )
613 {
614 wxString libNickname = *it;
615 XNODE* xlibrary;
616
617 if( symbolLibTable->HasLibrary( libNickname ) )
618 {
619 xlibs->AddChild( xlibrary = node( wxT( "library" ) ) );
620 xlibrary->AddAttribute( wxT( "logical" ), libNickname );
621 xlibrary->AddChild( node( wxT( "uri" ), symbolLibTable->GetFullURI( libNickname ) ) );
622 }
623
624 // @todo: add more fun stuff here
625 }
626
627 return xlibs;
628}
629
630
632{
633 XNODE* xlibparts = node( wxT( "libparts" ) ); // auto_ptr
634 std::vector<SCH_FIELD*> fieldList;
635
636 m_libraries.clear();
637
638 for( LIB_SYMBOL* lcomp : m_libParts )
639 {
640 wxString libNickname = lcomp->GetLibId().GetLibNickname();;
641
642 // The library nickname will be empty if the cache library is used.
643 if( !libNickname.IsEmpty() )
644 m_libraries.insert( libNickname ); // inserts symbol's library if unique
645
646 XNODE* xlibpart;
647 xlibparts->AddChild( xlibpart = node( wxT( "libpart" ) ) );
648 xlibpart->AddAttribute( wxT( "lib" ), libNickname );
649 xlibpart->AddAttribute( wxT( "part" ), lcomp->GetName() );
650
651 //----- show the important properties -------------------------
652 if( !lcomp->GetDescription().IsEmpty() )
653 xlibpart->AddChild( node( wxT( "description" ), lcomp->GetDescription() ) );
654
655 if( !lcomp->GetDatasheetField().GetText().IsEmpty() )
656 xlibpart->AddChild( node( wxT( "docs" ), lcomp->GetDatasheetField().GetText() ) );
657
658 // Write the footprint list
659 if( lcomp->GetFPFilters().GetCount() )
660 {
661 XNODE* xfootprints;
662 xlibpart->AddChild( xfootprints = node( wxT( "footprints" ) ) );
663
664 for( unsigned i = 0; i < lcomp->GetFPFilters().GetCount(); ++i )
665 xfootprints->AddChild( node( wxT( "fp" ), lcomp->GetFPFilters()[i] ) );
666 }
667
668 //----- show the fields here ----------------------------------
669 fieldList.clear();
670 lcomp->GetFields( fieldList );
671
672 XNODE* xfields;
673 xlibpart->AddChild( xfields = node( "fields" ) );
674
675 for( const SCH_FIELD* field : fieldList )
676 {
677 XNODE* xfield;
678 xfields->AddChild( xfield = node( wxT( "field" ), field->GetText() ) );
679 xfield->AddAttribute( wxT( "name" ), field->GetCanonicalName() );
680 }
681
682 //----- show the pins here ------------------------------------
683 std::vector<SCH_PIN*> pinList = lcomp->GetPins( 0, 0 );
684
685 /*
686 * We must erase redundant Pins references in pinList
687 * These redundant pins exist because some pins are found more than one time when a
688 * symbol has multiple parts per package or has 2 representations (DeMorgan conversion).
689 * For instance, a 74ls00 has DeMorgan conversion, with different pin shapes, and
690 * therefore each pin appears 2 times in the list. Common pins (VCC, GND) can also be
691 * found more than once.
692 */
693 sort( pinList.begin(), pinList.end(), sortPinsByNumber );
694
695 for( int ii = 0; ii < (int)pinList.size()-1; ii++ )
696 {
697 if( pinList[ii]->GetNumber() == pinList[ii+1]->GetNumber() )
698 { // 2 pins have the same number, remove the redundant pin at index i+1
699 pinList.erase(pinList.begin() + ii + 1);
700 ii--;
701 }
702 }
703
704 if( pinList.size() )
705 {
706 XNODE* pins;
707
708 xlibpart->AddChild( pins = node( wxT( "pins" ) ) );
709
710 for( unsigned i=0; i<pinList.size(); ++i )
711 {
712 XNODE* pin;
713
714 pins->AddChild( pin = node( wxT( "pin" ) ) );
715 pin->AddAttribute( wxT( "num" ), pinList[i]->GetShownNumber() );
716 pin->AddAttribute( wxT( "name" ), pinList[i]->GetShownName() );
717 pin->AddAttribute( wxT( "type" ), pinList[i]->GetCanonicalElectricalTypeName() );
718
719 // caution: construction work site here, drive slowly
720 }
721 }
722 }
723
724 return xlibparts;
725}
726
727
729{
730 XNODE* xnets = node( wxT( "nets" ) ); // auto_ptr if exceptions ever get used.
731 wxString netCodeTxt;
732 wxString netName;
733 wxString ref;
734
735 XNODE* xnet = nullptr;
736
737 /* output:
738 <net code="123" name="/cfcard.sch/WAIT#" class="signal">
739 <node ref="R23" pin="1"/>
740 <node ref="U18" pin="12"/>
741 </net>
742 */
743
744 struct NET_NODE
745 {
746 NET_NODE( SCH_PIN* aPin, const SCH_SHEET_PATH& aSheet ) :
747 m_Pin( aPin ),
748 m_Sheet( aSheet )
749 {}
750
751 SCH_PIN* m_Pin;
752 SCH_SHEET_PATH m_Sheet;
753 };
754
755 struct NET_RECORD
756 {
757 NET_RECORD( const wxString& aName ) :
758 m_Name( aName ),
759 m_HasNoConnect( false )
760 {};
761
762 wxString m_Name;
763 wxString m_Class;
764 bool m_HasNoConnect;
765 std::vector<NET_NODE> m_Nodes;
766 };
767
768 std::vector<NET_RECORD*> nets;
769
770 for( const auto& [ key, subgraphs ] : m_schematic->ConnectionGraph()->GetNetMap() )
771 {
772 wxString net_name = key.Name;
773 NET_RECORD* net_record = nullptr;
774
775 if( !( aCtl & GNL_OPT_KICAD ) )
776 net_name = UnescapeString( net_name );
777
778 if( subgraphs.empty() )
779 continue;
780
781 nets.emplace_back( new NET_RECORD( net_name ) );
782 net_record = nets.back();
783
784 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
785 {
786 bool nc = subgraph->GetNoConnect() && subgraph->GetNoConnect()->Type() == SCH_NO_CONNECT_T;
787 const SCH_SHEET_PATH& sheet = subgraph->GetSheet();
788
789 if( net_record->m_Class.IsEmpty() && subgraph->GetDriver() )
790 {
791 if( subgraph->GetDriver()->GetEffectiveNetClass() )
792 {
793 net_record->m_Class = subgraph->GetDriver()->GetEffectiveNetClass()->GetName();
794 net_record->m_Class = UnescapeString( net_record->m_Class );
795 }
796 }
797
798 if( nc )
799 net_record->m_HasNoConnect = true;
800
801 for( SCH_ITEM* item : subgraph->GetItems() )
802 {
803 if( item->Type() == SCH_PIN_T )
804 {
805 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
806 SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( pin->GetParentSymbol() );
807 bool forBOM = aCtl & GNL_OPT_BOM;
808 bool forBoard = aCtl & GNL_OPT_KICAD;
809
810 if( !symbol )
811 continue;
812
813 if( forBOM && ( sheet.GetExcludedFromBOM() || symbol->GetExcludedFromBOM() ) )
814 continue;
815
816 if( forBoard && ( sheet.GetExcludedFromBoard() || symbol->GetExcludedFromBoard() ) )
817 continue;
818
819 net_record->m_Nodes.emplace_back( pin, sheet );
820 }
821 }
822 }
823 }
824
825 // Netlist ordering: Net name, then ref des, then pin name
826 std::sort( nets.begin(), nets.end(),
827 []( const NET_RECORD* a, const NET_RECORD*b )
828 {
829 return StrNumCmp( a->m_Name, b->m_Name ) < 0;
830 } );
831
832 for( int i = 0; i < (int) nets.size(); ++i )
833 {
834 NET_RECORD* net_record = nets[i];
835 bool added = false;
836 XNODE* xnode;
837
838 // Netlist ordering: Net name, then ref des, then pin name
839 std::sort( net_record->m_Nodes.begin(), net_record->m_Nodes.end(),
840 []( const NET_NODE& a, const NET_NODE& b )
841 {
842 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
843 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
844
845 if( refA == refB )
846 return a.m_Pin->GetShownNumber() < b.m_Pin->GetShownNumber();
847
848 return refA < refB;
849 } );
850
851 // Some duplicates can exist, for example on multi-unit parts with duplicated pins across
852 // units. If the user connects the pins on each unit, they will appear on separate
853 // subgraphs. Remove those here:
854 alg::remove_duplicates( net_record->m_Nodes,
855 []( const NET_NODE& a, const NET_NODE& b )
856 {
857 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
858 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
859
860 return refA == refB && a.m_Pin->GetShownNumber() == b.m_Pin->GetShownNumber();
861 } );
862
863 // Determine if all pins in the net are stacked (nets with only one pin are implicitly
864 // taken to be stacked)
865 bool allNetPinsStacked = true;
866
867 if( net_record->m_Nodes.size() > 1 )
868 {
869 SCH_PIN* firstPin = net_record->m_Nodes.begin()->m_Pin;
870 allNetPinsStacked =
871 std::all_of( net_record->m_Nodes.begin() + 1, net_record->m_Nodes.end(),
872 [=]( auto& node )
873 {
874 return firstPin->GetParent() == node.m_Pin->GetParent()
875 && firstPin->GetPosition() == node.m_Pin->GetPosition()
876 && firstPin->GetName() == node.m_Pin->GetName();
877 } );
878 }
879
880 for( const NET_NODE& netNode : net_record->m_Nodes )
881 {
882 wxString refText = netNode.m_Pin->GetParentSymbol()->GetRef( &netNode.m_Sheet );
883 wxString pinText = netNode.m_Pin->GetShownNumber();
884
885 // Skip power symbols and virtual symbols
886 if( refText[0] == wxChar( '#' ) )
887 continue;
888
889 if( !added )
890 {
891 netCodeTxt.Printf( wxT( "%d" ), i + 1 );
892
893 xnets->AddChild( xnet = node( wxT( "net" ) ) );
894 xnet->AddAttribute( wxT( "code" ), netCodeTxt );
895 xnet->AddAttribute( wxT( "name" ), net_record->m_Name );
896 xnet->AddAttribute( wxT( "class" ), net_record->m_Class );
897
898 added = true;
899 }
900
901 xnet->AddChild( xnode = node( wxT( "node" ) ) );
902 xnode->AddAttribute( wxT( "ref" ), refText );
903 xnode->AddAttribute( wxT( "pin" ), pinText );
904
905 wxString pinName = netNode.m_Pin->GetShownName();
906 wxString pinType = netNode.m_Pin->GetCanonicalElectricalTypeName();
907
908 if( !pinName.IsEmpty() )
909 xnode->AddAttribute( wxT( "pinfunction" ), pinName );
910
911 if( net_record->m_HasNoConnect
912 && ( net_record->m_Nodes.size() == 1 || allNetPinsStacked ) )
913 pinType += wxT( "+no_connect" );
914
915 xnode->AddAttribute( wxT( "pintype" ), pinType );
916 }
917 }
918
919 for( NET_RECORD* record : nets )
920 delete record;
921
922 return xnets;
923}
924
925
926XNODE* NETLIST_EXPORTER_XML::node( const wxString& aName,
927 const wxString& aTextualContent /* = wxEmptyString*/ )
928{
929 XNODE* n = new XNODE( wxXML_ELEMENT_NODE, aName );
930
931 if( aTextualContent.Len() > 0 ) // excludes wxEmptyString, the parameter's default value
932 n->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) );
933
934 return n;
935}
936
937
938static bool sortPinsByNumber( SCH_PIN* aPin1, SCH_PIN* aPin2 )
939{
940 // return "lhs < rhs"
941 return StrNumCmp( aPin1->GetShownNumber(), aPin2->GetShownNumber(), true ) < 0;
942}
wxString GetBuildVersion()
Get the full KiCad version string.
const NET_MAP & GetNetMap() const
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:89
const KIID m_Uuid
Definition: eda_item.h:490
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:98
wxString AsString() const
Definition: kiid.cpp:246
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:84
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
wxString GetFullURI(const wxString &aLibNickname, bool aExpandEnvVars=true) const
Return the full URI of the library mapped to aLibNickname.
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.
SCHEMATIC_IFACE * m_schematic
The schematic we're generating a netlist for.
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.
static SYMBOL_LIB_TABLE * SchSymbolLibTable(PROJECT *aProject)
Accessor for project symbol library table.
Container for project specific data.
Definition: project.h:64
virtual std::map< wxString, wxString > & GetTextVars() const
Definition: project.cpp:95
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:72
virtual void SetCurrentSheet(const SCH_SHEET_PATH &aPath)=0
virtual wxString GetFileName() const =0
virtual CONNECTION_GRAPH * ConnectionGraph() const =0
virtual SCH_SHEET_PATH & CurrentSheet() const =0
virtual PROJECT & Prj() const =0
virtual SCH_SHEET_LIST Hierarchy() const =0
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:51
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
Definition: sch_field.cpp:213
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:167
wxString GetShownNumber() const
Definition: sch_pin.cpp:518
const wxString & GetFileName() const
Definition: sch_screen.h:143
const TITLE_BLOCK & GetTitleBlock() const
Definition: sch_screen.h:154
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
Schematic symbol object.
Definition: sch_symbol.h:77
wxString GetDescription() const override
Definition: sch_symbol.cpp:260
bool UseLibIdLookup() const
Definition: sch_symbol.h:183
wxString GetSchSymbolLibraryName() const
Definition: sch_symbol.cpp:242
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) override
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:954
SCH_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: sch_symbol.cpp:906
const wxString GetValue(bool aResolve, const SCH_SHEET_PATH *aPath, bool aAllowExtraText) const override
Definition: sch_symbol.cpp:874
const wxString GetFootprintFieldText(bool aResolve, const SCH_SHEET_PATH *aPath, bool aAllowExtraText) const
Definition: sch_symbol.cpp:890
const LIB_ID & GetLibId() const override
Definition: sch_symbol.h:166
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
Definition: sch_symbol.cpp:832
int GetUnitCount() const override
Return the number of units per package of the symbol.
Definition: sch_symbol.cpp:434
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition: sch_symbol.h:185
std::unordered_set< wxString > GetComponentClassNames(const SCH_SHEET_PATH *aPath) const
Returns the component classes this symbol belongs in.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
Definition: sch_symbol.cpp:704
bool GetExcludedFromBoard() const
Definition: symbol.h:181
bool GetExcludedFromBOM() const
Definition: symbol.h:175
bool GetDNP() const
Set or clear the 'Do Not Populate' flag.
Definition: symbol.h:186
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
Definition: title_block.h:107
const wxString & GetTitle() const
Definition: title_block.h:63
void Clear()
Erase the record.
Hold an XML or S-expression element.
Definition: xnode.h:43
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition: common.cpp:59
The common library.
void remove_duplicates(_Container &__c)
Deletes all duplicate values from __c.
Definition: kicad_algo.h:183
static bool sortPinsByNumber(SCH_PIN *aPin1, SCH_PIN *aPin2)
#define GNL_ALL
@ GNL_PARTS
@ GNL_LIBRARIES
@ GNL_OPT_KICAD
@ GNL_SYMBOLS
@ GNL_HEADER
@ GNL_OPT_BOM
@ GNL_NETS
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
wxString UnescapeString(const wxString &aSource)
wxString GetISO8601CurrentDateTime()
Definition for symbol library class.
wxString GetCanonicalFieldName(int idx)
@ DATASHEET_FIELD
name of datasheet
@ FOOTPRINT_FIELD
Field Name Module PCB, i.e. "16DIP300".
@ MANDATORY_FIELDS
The first 5 are mandatory, and must be instantiated in SCH_COMPONENT, LIB_PART, and FOOTPRINT constru...
@ DESCRIPTION_FIELD
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ SCH_NO_CONNECT_T
Definition: typeinfo.h:160
@ SCH_SYMBOL_T
Definition: typeinfo.h:172
@ SCH_PIN_T
Definition: typeinfo.h:153