KiCad PCB EDA Suite
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 (C) 1992-2022 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
38#include <symbol_lib_table.h>
39
40#include <set>
41
42static bool sortPinsByNumber( LIB_PIN* aPin1, LIB_PIN* aPin2 );
43
44bool NETLIST_EXPORTER_XML::WriteNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
45 REPORTER& aReporter )
46{
47 // output the XML format netlist.
48
49 // declare the stream ourselves to use the buffered FILE api
50 // instead of letting wx use the syscall variant
51 wxFFileOutputStream stream( aOutFileName );
52
53 if( !stream.IsOk() )
54 return false;
55
56 wxXmlDocument xdoc;
57 xdoc.SetRoot( makeRoot( GNL_ALL | aNetlistOptions ) );
58
59 return xdoc.Save( stream, 2 /* indent bug, today was ignored by wxXml lib */ );
60}
61
62
64{
65 XNODE* xroot = node( wxT( "export" ) );
66
67 xroot->AddAttribute( wxT( "version" ), wxT( "E" ) );
68
69 if( aCtl & GNL_HEADER )
70 // add the "design" header
71 xroot->AddChild( makeDesignHeader() );
72
73 if( aCtl & GNL_SYMBOLS )
74 xroot->AddChild( makeSymbols( aCtl ) );
75
76 if( aCtl & GNL_PARTS )
77 xroot->AddChild( makeLibParts() );
78
79 if( aCtl & GNL_LIBRARIES )
80 // must follow makeGenericLibParts()
81 xroot->AddChild( makeLibraries() );
82
83 if( aCtl & GNL_NETS )
84 xroot->AddChild( makeListOfNets( aCtl ) );
85
86 return xroot;
87}
88
89
91
92
94 SCH_SHEET_PATH* aSheet )
95{
96 wxString value;
97 wxString datasheet;
98 wxString footprint;
99 wxString candidate;
100 std::map<wxString, wxString> userFields;
101
102 if( aSymbol->GetUnitCount() > 1 )
103 {
104 // Sadly, each unit of a symbol can have its own unique fields. This
105 // block finds the unit with the lowest number having a non blank field
106 // value and records it. Therefore user is best off setting fields
107 // into only the first unit. But this scavenger algorithm will find
108 // any non blank fields in all units and use the first non-blank field
109 // for each unique field name.
110
111 wxString ref = aSymbol->GetRef( aSheet );
112
113 SCH_SHEET_LIST sheetList = m_schematic->GetSheets();
114 int minUnit = aSymbol->GetUnitSelection( aSheet );
115
116 for( unsigned i = 0; i < sheetList.size(); i++ )
117 {
118 for( SCH_ITEM* item : sheetList[i].LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
119 {
120 SCH_SYMBOL* symbol2 = (SCH_SYMBOL*) item;
121
122 wxString ref2 = symbol2->GetRef( &sheetList[i] );
123
124 if( ref2.CmpNoCase( ref ) != 0 )
125 continue;
126
127 int unit = symbol2->GetUnitSelection( aSheet );
128
129 // The lowest unit number wins. User should only set fields in any one unit.
130 candidate = symbol2->GetValueFieldText( m_resolveTextVars );
131
132 if( !candidate.IsEmpty() && ( unit < minUnit || value.IsEmpty() ) )
133 value = candidate;
134
135 candidate = symbol2->GetFootprintFieldText( m_resolveTextVars );
136
137 if( !candidate.IsEmpty() && ( unit < minUnit || footprint.IsEmpty() ) )
138 footprint = candidate;
139
140 candidate = m_resolveTextVars ? symbol2->GetField( DATASHEET_FIELD )->GetShownText()
141 : symbol2->GetField( DATASHEET_FIELD )->GetText();
142
143 if( !candidate.IsEmpty() && ( unit < minUnit || datasheet.IsEmpty() ) )
144 datasheet = candidate;
145
146 for( int ii = MANDATORY_FIELDS; ii < symbol2->GetFieldCount(); ++ii )
147 {
148 const SCH_FIELD& f = symbol2->GetFields()[ ii ];
149
150 if( f.GetText().size()
151 && ( unit < minUnit || userFields.count( f.GetName() ) == 0 ) )
152 {
154 userFields[ f.GetName() ] = f.GetShownText();
155 else
156 userFields[ f.GetName() ] = f.GetText();
157 }
158 }
159
160 minUnit = std::min( unit, minUnit );
161 }
162 }
163 }
164 else
165 {
166 value = aSymbol->GetValueFieldText( m_resolveTextVars );
167 footprint = aSymbol->GetFootprintFieldText( m_resolveTextVars );
168
171 else
172 datasheet = aSymbol->GetField( DATASHEET_FIELD )->GetText();
173
174 for( int ii = MANDATORY_FIELDS; ii < aSymbol->GetFieldCount(); ++ii )
175 {
176 const SCH_FIELD& f = aSymbol->GetFields()[ ii ];
177
178 if( f.GetText().size() )
179 {
181 userFields[ f.GetName() ] = f.GetShownText();
182 else
183 userFields[ f.GetName() ] = f.GetText();
184 }
185 }
186 }
187
188 // Do not output field values blank in netlist:
189 if( value.size() )
190 aNode->AddChild( node( wxT( "value" ), UnescapeString( value ) ) );
191 else // value field always written in netlist
192 aNode->AddChild( node( wxT( "value" ), wxT( "~" ) ) );
193
194 if( footprint.size() )
195 aNode->AddChild( node( wxT( "footprint" ), UnescapeString( footprint ) ) );
196
197 if( datasheet.size() )
198 aNode->AddChild( node( wxT( "datasheet" ), UnescapeString( datasheet ) ) );
199
200 if( userFields.size() )
201 {
202 XNODE* xfields;
203 aNode->AddChild( xfields = node( wxT( "fields" ) ) );
204
205 // non MANDATORY fields are output alphabetically
206 for( const std::pair<const wxString, wxString>& f : userFields )
207 {
208 XNODE* xfield = node( wxT( "field" ), UnescapeString( f.second ) );
209 xfield->AddAttribute( wxT( "name" ), UnescapeString( f.first ) );
210 xfields->AddChild( xfield );
211 }
212 }
213}
214
215
217{
218 XNODE* xcomps = node( wxT( "components" ) );
219
221 m_libParts.clear();
222
223 SCH_SHEET_LIST sheetList = m_schematic->GetSheets();
224
225 // Output is xml, so there is no reason to remove spaces from the field values.
226 // And XML element names need not be translated to various languages.
227
228 for( unsigned ii = 0; ii < sheetList.size(); ii++ )
229 {
230 SCH_SHEET_PATH sheet = sheetList[ii];
231
232 auto cmp = [sheet]( SCH_SYMBOL* a, SCH_SYMBOL* b )
233 {
234 return ( StrNumCmp( a->GetRef( &sheet, false ),
235 b->GetRef( &sheet, false ), true ) < 0 );
236 };
237
238 std::set<SCH_SYMBOL*, decltype( cmp )> ordered_symbols( cmp );
239 std::multiset<SCH_SYMBOL*, decltype( cmp )> extra_units( cmp );
240
241 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
242 {
243 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
244 auto test = ordered_symbols.insert( symbol );
245
246 if( !test.second )
247 {
248 if( ( *( test.first ) )->m_Uuid > symbol->m_Uuid )
249 {
250 extra_units.insert( *( test.first ) );
251 ordered_symbols.erase( test.first );
252 ordered_symbols.insert( symbol );
253 }
254 else
255 {
256 extra_units.insert( symbol );
257 }
258 }
259 }
260
261 for( EDA_ITEM* item : ordered_symbols )
262 {
263 SCH_SYMBOL* symbol = findNextSymbol( item, &sheet );
264
265 if( !symbol
266 || ( ( aCtl & GNL_OPT_BOM ) && !symbol->GetIncludeInBom() )
267 || ( ( aCtl & GNL_OPT_KICAD ) && !symbol->GetIncludeOnBoard() ) )
268 {
269 continue;
270 }
271
272 // Output the symbol's elements in order of expected access frequency. This may
273 // not always look best, but it will allow faster execution under XSL processing
274 // systems which do sequential searching within an element.
275
276 XNODE* xcomp; // current symbol being constructed
277 xcomps->AddChild( xcomp = node( wxT( "comp" ) ) );
278
279 xcomp->AddAttribute( wxT( "ref" ), symbol->GetRef( &sheet ) );
280 addSymbolFields( xcomp, symbol, &sheet );
281
282 XNODE* xlibsource;
283 xcomp->AddChild( xlibsource = node( wxT( "libsource" ) ) );
284
285 // "logical" library name, which is in anticipation of a better search algorithm
286 // for parts based on "logical_lib.part" and where logical_lib is merely the library
287 // name minus path and extension.
288 wxString libName;
289 wxString partName;
290
291 if( symbol->UseLibIdLookup() )
292 {
293 libName = symbol->GetLibId().GetLibNickname();
294 partName = symbol->GetLibId().GetLibItemName();
295 }
296 else
297 {
298 partName = symbol->GetSchSymbolLibraryName();
299 }
300
301 xlibsource->AddAttribute( wxT( "lib" ), libName );
302
303 // We only want the symbol name, not the full LIB_ID.
304 xlibsource->AddAttribute( wxT( "part" ), partName );
305
306 xlibsource->AddAttribute( wxT( "description" ), symbol->GetDescription() );
307
308 XNODE* xproperty;
309
310 std::vector<SCH_FIELD>& fields = symbol->GetFields();
311
312 for( size_t jj = MANDATORY_FIELDS; jj < fields.size(); ++jj )
313 {
314 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
315 xproperty->AddAttribute( wxT( "name" ), fields[jj].GetCanonicalName() );
316
318 xproperty->AddAttribute( wxT( "value" ), fields[jj].GetShownText() );
319 else
320 xproperty->AddAttribute( wxT( "value" ), fields[jj].GetText() );
321 }
322
323 for( const SCH_FIELD& sheetField : sheet.Last()->GetFields() )
324 {
325 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
326 xproperty->AddAttribute( wxT( "name" ), sheetField.GetCanonicalName() );
327
329 // do not allow GetShownText() to add any prefix useful only when displaying
330 // the field on screen
331 xproperty->AddAttribute( wxT( "value" ), sheetField.GetShownText( 0, false ) );
332 else
333 xproperty->AddAttribute( wxT( "value" ), sheetField.GetText() );
334 }
335
336 if( !symbol->GetIncludeInBom() )
337 {
338 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
339 xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_bom" ) );
340 }
341
342 if( !symbol->GetIncludeOnBoard() )
343 {
344 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
345 xproperty->AddAttribute( wxT( "name" ), wxT( "exclude_from_board" ) );
346 }
347
348 if( symbol->GetDNP() )
349 {
350 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
351 xproperty->AddAttribute( wxT( "name" ), wxT( "dnp" ) );
352 }
353
354 if( const std::unique_ptr<LIB_SYMBOL>& part = symbol->GetLibSymbolRef() )
355 {
356 if( part->GetDescription().size() )
357 {
358 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
359 xproperty->AddAttribute( wxT( "name" ), wxT( "ki_description" ) );
360 xproperty->AddAttribute( wxT( "value" ), part->GetDescription() );
361 }
362
363 if( part->GetKeyWords().size() )
364 {
365 xcomp->AddChild( xproperty = node( wxT( "property" ) ) );
366 xproperty->AddAttribute( wxT( "name" ), wxT( "ki_keywords" ) );
367 xproperty->AddAttribute( wxT( "value" ), part->GetKeyWords() );
368 }
369 }
370
371 XNODE* xsheetpath;
372 xcomp->AddChild( xsheetpath = node( wxT( "sheetpath" ) ) );
373
374 xsheetpath->AddAttribute( wxT( "names" ), sheet.PathHumanReadable() );
375 xsheetpath->AddAttribute( wxT( "tstamps" ), sheet.PathAsString() );
376
377 XNODE* xunits; // Node for extra units
378 xcomp->AddChild( xunits = node( wxT( "tstamps" ) ) );
379
380 auto range = extra_units.equal_range( symbol );
381 wxString uuid;
382
383 // Output a series of children with all UUIDs associated with the REFDES
384 for( auto it = range.first; it != range.second; ++it )
385 {
386 uuid = ( *it )->m_Uuid.AsString();
387
388 // Add a space between UUIDs, if not in KICAD mode (i.e.
389 // using wxXmlDocument::Save()). KICAD MODE has its own XNODE::Format function.
390 if( !( aCtl & GNL_OPT_KICAD ) ) // i.e. for .xml format
391 uuid += ' ';
392
393 xunits->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, uuid ) );
394 }
395
396 // Output the primary UUID
397 uuid = symbol->m_Uuid.AsString();
398 xunits->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, uuid ) );
399 }
400 }
401
402 return xcomps;
403}
404
405
407{
408 SCH_SCREEN* screen;
409 XNODE* xdesign = node( wxT( "design" ) );
410 XNODE* xtitleBlock;
411 XNODE* xsheet;
412 XNODE* xcomment;
413 XNODE* xtextvar;
414 wxString sheetTxt;
415 wxFileName sourceFileName;
416
417 // the root sheet is a special sheet, call it source
418 xdesign->AddChild( node( wxT( "source" ), m_schematic->GetFileName() ) );
419
420 xdesign->AddChild( node( wxT( "date" ), DateAndTime() ) );
421
422 // which Eeschema tool
423 xdesign->AddChild( node( wxT( "tool" ), wxT( "Eeschema " ) + GetBuildVersion() ) );
424
425 const std::map<wxString, wxString>& properties = m_schematic->Prj().GetTextVars();
426
427 for( const std::pair<const wxString, wxString>& prop : properties )
428 {
429 xdesign->AddChild( xtextvar = node( wxT( "textvar" ), prop.second ) );
430 xtextvar->AddAttribute( wxT( "name" ), prop.first );
431 }
432
433 /*
434 * Export the sheets information
435 */
436 SCH_SHEET_LIST sheetList = m_schematic->GetSheets();
437
438 for( unsigned i = 0; i < sheetList.size(); i++ )
439 {
440 screen = sheetList[i].LastScreen();
441
442 xdesign->AddChild( xsheet = node( wxT( "sheet" ) ) );
443
444 // get the string representation of the sheet index number.
445 // Note that sheet->GetIndex() is zero index base and we need to increment the
446 // number by one to make it human readable
447 sheetTxt.Printf( wxT( "%u" ), i + 1 );
448 xsheet->AddAttribute( wxT( "number" ), sheetTxt );
449 xsheet->AddAttribute( wxT( "name" ), sheetList[i].PathHumanReadable() );
450 xsheet->AddAttribute( wxT( "tstamps" ), sheetList[i].PathAsString() );
451
452 TITLE_BLOCK tb = screen->GetTitleBlock();
453 PROJECT* prj = &m_schematic->Prj();
454
455 xsheet->AddChild( xtitleBlock = node( wxT( "title_block" ) ) );
456
457 xtitleBlock->AddChild( node( wxT( "title" ), ExpandTextVars( tb.GetTitle(), prj ) ) );
458 xtitleBlock->AddChild( node( wxT( "company" ), ExpandTextVars( tb.GetCompany(), prj ) ) );
459 xtitleBlock->AddChild( node( wxT( "rev" ), ExpandTextVars( tb.GetRevision(), prj ) ) );
460 xtitleBlock->AddChild( node( wxT( "date" ), ExpandTextVars( tb.GetDate(), prj ) ) );
461
462 // We are going to remove the fileName directories.
463 sourceFileName = wxFileName( screen->GetFileName() );
464 xtitleBlock->AddChild( node( wxT( "source" ), sourceFileName.GetFullName() ) );
465
466 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
467 xcomment->AddAttribute( wxT( "number" ), wxT( "1" ) );
468 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 0 ), prj ) );
469
470 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
471 xcomment->AddAttribute( wxT( "number" ), wxT( "2" ) );
472 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 1 ), prj ) );
473
474 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
475 xcomment->AddAttribute( wxT( "number" ), wxT( "3" ) );
476 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 2 ), prj ) );
477
478 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
479 xcomment->AddAttribute( wxT( "number" ), wxT( "4" ) );
480 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 3 ), prj ) );
481
482 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
483 xcomment->AddAttribute( wxT( "number" ), wxT( "5" ) );
484 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 4 ), prj ) );
485
486 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
487 xcomment->AddAttribute( wxT( "number" ), wxT( "6" ) );
488 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 5 ), prj ) );
489
490 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
491 xcomment->AddAttribute( wxT( "number" ), wxT( "7" ) );
492 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 6 ), prj ) );
493
494 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
495 xcomment->AddAttribute( wxT( "number" ), wxT( "8" ) );
496 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 7 ), prj ) );
497
498 xtitleBlock->AddChild( xcomment = node( wxT( "comment" ) ) );
499 xcomment->AddAttribute( wxT( "number" ), wxT( "9" ) );
500 xcomment->AddAttribute( wxT( "value" ), ExpandTextVars( tb.GetComment( 8 ), prj ) );
501 }
502
503 return xdesign;
504}
505
506
508{
509 XNODE* xlibs = node( wxT( "libraries" ) ); // auto_ptr
510 SYMBOL_LIB_TABLE* symbolLibTable = m_schematic->Prj().SchSymbolLibTable();
511
512 for( std::set<wxString>::iterator it = m_libraries.begin(); it!=m_libraries.end(); ++it )
513 {
514 wxString libNickname = *it;
515 XNODE* xlibrary;
516
517 if( symbolLibTable->HasLibrary( libNickname ) )
518 {
519 xlibs->AddChild( xlibrary = node( wxT( "library" ) ) );
520 xlibrary->AddAttribute( wxT( "logical" ), libNickname );
521 xlibrary->AddChild( node( wxT( "uri" ), symbolLibTable->GetFullURI( libNickname ) ) );
522 }
523
524 // @todo: add more fun stuff here
525 }
526
527 return xlibs;
528}
529
530
532{
533 XNODE* xlibparts = node( wxT( "libparts" ) ); // auto_ptr
534
535 LIB_PINS pinList;
536 std::vector<LIB_FIELD*> fieldList;
537
538 m_libraries.clear();
539
540 for( LIB_SYMBOL* lcomp : m_libParts )
541 {
542 wxString libNickname = lcomp->GetLibId().GetLibNickname();;
543
544 // The library nickname will be empty if the cache library is used.
545 if( !libNickname.IsEmpty() )
546 m_libraries.insert( libNickname ); // inserts symbol's library if unique
547
548 XNODE* xlibpart;
549 xlibparts->AddChild( xlibpart = node( wxT( "libpart" ) ) );
550 xlibpart->AddAttribute( wxT( "lib" ), libNickname );
551 xlibpart->AddAttribute( wxT( "part" ), lcomp->GetName() );
552
553 //----- show the important properties -------------------------
554 if( !lcomp->GetDescription().IsEmpty() )
555 xlibpart->AddChild( node( wxT( "description" ), lcomp->GetDescription() ) );
556
557 if( !lcomp->GetDatasheetField().GetText().IsEmpty() )
558 xlibpart->AddChild( node( wxT( "docs" ), lcomp->GetDatasheetField().GetText() ) );
559
560 // Write the footprint list
561 if( lcomp->GetFPFilters().GetCount() )
562 {
563 XNODE* xfootprints;
564 xlibpart->AddChild( xfootprints = node( wxT( "footprints" ) ) );
565
566 for( unsigned i = 0; i < lcomp->GetFPFilters().GetCount(); ++i )
567 xfootprints->AddChild( node( wxT( "fp" ), lcomp->GetFPFilters()[i] ) );
568 }
569
570 //----- show the fields here ----------------------------------
571 fieldList.clear();
572 lcomp->GetFields( fieldList );
573
574 XNODE* xfields;
575 xlibpart->AddChild( xfields = node( "fields" ) );
576
577 for( const LIB_FIELD* field : fieldList )
578 {
579 if( !field->GetText().IsEmpty() )
580 {
581 XNODE* xfield;
582 xfields->AddChild( xfield = node( wxT( "field" ), field->GetText() ) );
583 xfield->AddAttribute( wxT( "name" ), field->GetCanonicalName() );
584 }
585 }
586
587 //----- show the pins here ------------------------------------
588 pinList.clear();
589 lcomp->GetPins( pinList, 0, 0 );
590
591 /*
592 * We must erase redundant Pins references in pinList
593 * These redundant pins exist because some pins are found more than one time when a
594 * symbol has multiple parts per package or has 2 representations (DeMorgan conversion).
595 * For instance, a 74ls00 has DeMorgan conversion, with different pin shapes, and
596 * therefore each pin appears 2 times in the list. Common pins (VCC, GND) can also be
597 * found more than once.
598 */
599 sort( pinList.begin(), pinList.end(), sortPinsByNumber );
600 for( int ii = 0; ii < (int)pinList.size()-1; ii++ )
601 {
602 if( pinList[ii]->GetNumber() == pinList[ii+1]->GetNumber() )
603 { // 2 pins have the same number, remove the redundant pin at index i+1
604 pinList.erase(pinList.begin() + ii + 1);
605 ii--;
606 }
607 }
608
609 if( pinList.size() )
610 {
611 XNODE* pins;
612
613 xlibpart->AddChild( pins = node( wxT( "pins" ) ) );
614 for( unsigned i=0; i<pinList.size(); ++i )
615 {
616 XNODE* pin;
617
618 pins->AddChild( pin = node( wxT( "pin" ) ) );
619 pin->AddAttribute( wxT( "num" ), pinList[i]->GetShownNumber() );
620 pin->AddAttribute( wxT( "name" ), pinList[i]->GetShownName() );
621 pin->AddAttribute( wxT( "type" ), pinList[i]->GetCanonicalElectricalTypeName() );
622
623 // caution: construction work site here, drive slowly
624 }
625 }
626 }
627
628 return xlibparts;
629}
630
631
633{
634 XNODE* xnets = node( wxT( "nets" ) ); // auto_ptr if exceptions ever get used.
635 wxString netCodeTxt;
636 wxString netName;
637 wxString ref;
638
639 XNODE* xnet = nullptr;
640
641 /* output:
642 <net code="123" name="/cfcard.sch/WAIT#">
643 <node ref="R23" pin="1"/>
644 <node ref="U18" pin="12"/>
645 </net>
646 */
647
648 struct NET_NODE
649 {
650 NET_NODE( SCH_PIN* aPin, const SCH_SHEET_PATH& aSheet, bool aNoConnect ) :
651 m_Pin( aPin ),
652 m_Sheet( aSheet ),
653 m_NoConnect( aNoConnect )
654 {}
655
656 SCH_PIN* m_Pin;
657 SCH_SHEET_PATH m_Sheet;
658 bool m_NoConnect;
659 };
660
661 struct NET_RECORD
662 {
663 NET_RECORD( const wxString& aName ) :
664 m_Name( aName )
665 {};
666
667 wxString m_Name;
668 std::vector<NET_NODE> m_Nodes;
669 };
670
671 std::vector<NET_RECORD*> nets;
672
673 for( const auto& [ key, subgraphs ] : m_schematic->ConnectionGraph()->GetNetMap() )
674 {
675 wxString net_name = key.Name;
676 NET_RECORD* net_record = nullptr;
677
678 if( subgraphs.empty() )
679 continue;
680
681 nets.emplace_back( new NET_RECORD( net_name ) );
682 net_record = nets.back();
683
684 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
685 {
686 bool nc = subgraph->m_no_connect && subgraph->m_no_connect->Type() == SCH_NO_CONNECT_T;
687 const SCH_SHEET_PATH& sheet = subgraph->m_sheet;
688
689 for( SCH_ITEM* item : subgraph->m_items )
690 {
691 if( item->Type() == SCH_PIN_T )
692 {
693 SCH_PIN* pin = static_cast<SCH_PIN*>( item );
694 SCH_SYMBOL* symbol = pin->GetParentSymbol();
695
696 if( !symbol
697 || ( ( aCtl & GNL_OPT_BOM ) && !symbol->GetIncludeInBom() )
698 || ( ( aCtl & GNL_OPT_KICAD ) && !symbol->GetIncludeOnBoard() ) )
699 {
700 continue;
701 }
702
703 net_record->m_Nodes.emplace_back( pin, sheet, nc );
704 }
705 }
706 }
707 }
708
709 // Netlist ordering: Net name, then ref des, then pin name
710 std::sort( nets.begin(), nets.end(),
711 []( const NET_RECORD* a, const NET_RECORD*b )
712 {
713 return StrNumCmp( a->m_Name, b->m_Name ) < 0;
714 } );
715
716 for( int i = 0; i < (int) nets.size(); ++i )
717 {
718 NET_RECORD* net_record = nets[i];
719 bool added = false;
720 XNODE* xnode;
721
722 // Netlist ordering: Net name, then ref des, then pin name
723 std::sort( net_record->m_Nodes.begin(), net_record->m_Nodes.end(),
724 []( const NET_NODE& a, const NET_NODE& b )
725 {
726 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
727 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
728
729 if( refA == refB )
730 return a.m_Pin->GetShownNumber() < b.m_Pin->GetShownNumber();
731
732 return refA < refB;
733 } );
734
735 // Some duplicates can exist, for example on multi-unit parts with duplicated pins across
736 // units. If the user connects the pins on each unit, they will appear on separate
737 // subgraphs. Remove those here:
738 alg::remove_duplicates( net_record->m_Nodes,
739 []( const NET_NODE& a, const NET_NODE& b )
740 {
741 wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
742 wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
743
744 return refA == refB && a.m_Pin->GetShownNumber() == b.m_Pin->GetShownNumber();
745 } );
746
747 for( const NET_NODE& netNode : net_record->m_Nodes )
748 {
749 wxString refText = netNode.m_Pin->GetParentSymbol()->GetRef( &netNode.m_Sheet );
750 wxString pinText = netNode.m_Pin->GetShownNumber();
751
752 // Skip power symbols and virtual symbols
753 if( refText[0] == wxChar( '#' ) )
754 continue;
755
756 if( !added )
757 {
758 netCodeTxt.Printf( wxT( "%d" ), i + 1 );
759
760 xnets->AddChild( xnet = node( wxT( "net" ) ) );
761 xnet->AddAttribute( wxT( "code" ), netCodeTxt );
762 xnet->AddAttribute( wxT( "name" ), net_record->m_Name );
763
764 added = true;
765 }
766
767 xnet->AddChild( xnode = node( wxT( "node" ) ) );
768 xnode->AddAttribute( wxT( "ref" ), refText );
769 xnode->AddAttribute( wxT( "pin" ), pinText );
770
771 wxString pinName = netNode.m_Pin->GetShownName();
772 wxString pinType = netNode.m_Pin->GetCanonicalElectricalTypeName();
773
774 if( !pinName.IsEmpty() )
775 xnode->AddAttribute( wxT( "pinfunction" ), pinName );
776
777 if( netNode.m_NoConnect )
778 pinType += wxT( "+no_connect" );
779
780 xnode->AddAttribute( wxT( "pintype" ), pinType );
781 }
782 }
783
784 for( NET_RECORD* record : nets )
785 delete record;
786
787 return xnets;
788}
789
790
791XNODE* NETLIST_EXPORTER_XML::node( const wxString& aName,
792 const wxString& aTextualContent /* = wxEmptyString*/ )
793{
794 XNODE* n = new XNODE( wxXML_ELEMENT_NODE, aName );
795
796 if( aTextualContent.Len() > 0 ) // excludes wxEmptyString, the parameter's default value
797 n->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) );
798
799 return n;
800}
801
802
803static bool sortPinsByNumber( LIB_PIN* aPin1, LIB_PIN* aPin2 )
804{
805 // return "lhs < rhs"
806 return StrNumCmp( aPin1->GetShownNumber(), aPin2->GetShownNumber(), true ) < 0;
807}
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:85
const KIID m_Uuid
Definition: eda_item.h:492
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:87
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:238
wxString AsString() const
Definition: kiid.cpp:257
Field object used in symbol libraries.
Definition: lib_field.h:61
const UTF8 & GetLibItemName() const
Definition: lib_id.h:102
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:87
wxString GetShownNumber() const
Definition: lib_pin.h:123
Define a library symbol object.
Definition: lib_symbol.h:99
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.
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.
SCH_SYMBOL * findNextSymbol(EDA_ITEM *aItem, SCH_SHEET_PATH *aSheetPath)
Check if the given symbol should be processed for netlisting.
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.
XNODE * makeSymbols(unsigned aCtl)
XNODE * makeRoot(unsigned aCtl=GNL_ALL)
Build the entire document tree for the generic export.
void addSymbolFields(XNODE *aNode, SCH_SYMBOL *aSymbol, SCH_SHEET_PATH *aSheet)
Holder for multi-unit symbol fields.
std::set< wxString > m_libraries
XNODE * makeLibParts()
Fill out an XML node with the unique library parts and returns it.
Container for project specific data.
Definition: project.h:64
virtual std::map< wxString, wxString > & GetTextVars() const
Definition: project.cpp:82
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual wxString GetFileName() const =0
virtual CONNECTION_GRAPH * ConnectionGraph() const =0
virtual SCH_SHEET_LIST GetSheets() const =0
virtual PROJECT & Prj() 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(int aDepth=0, bool aAllowExtraText=true) const override
Return the string actually shown after processing of the base text.
Definition: sch_field.cpp:175
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
Definition: sch_field.cpp:813
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:147
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:109
const wxString & GetFileName() const
Definition: sch_screen.h:144
const TITLE_BLOCK & GetTitleBlock() const
Definition: sch_screen.h:155
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...
wxString PathHumanReadable(bool aUseShortRootName=true, bool aStripTrailingSeparator=false) const
Return the sheet path in a human readable form made from the sheet names.
SCH_SCREEN * LastScreen()
wxString PathAsString() const
Return the path of time stamps which do not changes even when editing sheet parameters.
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
std::vector< SCH_FIELD > & GetFields()
Definition: sch_sheet.h:93
Schematic symbol object.
Definition: sch_symbol.h:81
int GetUnitCount() const
Return the number of units per package of the symbol.
Definition: sch_symbol.cpp:423
int GetFieldCount() const
Return the number of fields in this symbol.
Definition: sch_symbol.h:486
bool UseLibIdLookup() const
Definition: sch_symbol.h:190
wxString GetSchSymbolLibraryName() const
Definition: sch_symbol.cpp:290
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const
Return the reference for the given sheet path.
Definition: sch_symbol.cpp:652
bool GetIncludeOnBoard() const
Definition: sch_symbol.h:750
bool GetIncludeInBom() const
Definition: sch_symbol.h:747
SCH_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: sch_symbol.cpp:843
const wxString GetFootprintFieldText(bool aResolve) const
Definition: sch_symbol.cpp:828
wxString GetDescription() const
Definition: sch_symbol.cpp:308
const wxString GetValueFieldText(bool aResolve) const
Definition: sch_symbol.cpp:813
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
Definition: sch_symbol.cpp:771
const LIB_ID & GetLibId() const
Definition: sch_symbol.h:175
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:879
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition: sch_symbol.h:192
bool GetDNP() const
Definition: sch_symbol.h:753
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:44
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:58
The common library.
std::vector< LIB_PIN * > LIB_PINS
Helper for defining a list of pin object pointers.
Definition: lib_item.h:54
wxString GetText(EDA_UNITS aUnits, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Get the units string for a given units type.
Definition: eda_units.cpp:98
void remove_duplicates(_Container &__c)
Deletes all duplicate values from __c.
Definition: kicad_algo.h:182
static bool sortPinsByNumber(LIB_PIN *aPin1, LIB_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 DateAndTime()
wxString UnescapeString(const wxString &aSource)
Definition for symbol library class.
@ DATASHEET_FIELD
name of datasheet
@ MANDATORY_FIELDS
The first 4 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
@ SCH_NO_CONNECT_T
Definition: typeinfo.h:143
@ SCH_SYMBOL_T
Definition: typeinfo.h:156
@ SCH_PIN_T
Definition: typeinfo.h:159