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