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 <dick@softplc.com>
6  * Copyright (C) 1992-2021 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 
26 #include "netlist_exporter_xml.h"
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 <string_utils.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 
42 static bool sortPinsByNumber( LIB_PIN* aPin1, LIB_PIN* aPin2 );
43 
44 bool 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( "export" );
65 
66  xroot->AddAttribute( "version", "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( auto 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  {
145  if( m_resolveTextVars )
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  {
158  if( m_resolveTextVars )
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 
174  if( m_resolveTextVars )
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  {
185  if( m_resolveTextVars )
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( "value", UnescapeString( value ) ) );
196  else // value field always written in netlist
197  aNode->AddChild( node( "value", "~" ) );
198 
199  if( footprint.size() )
200  aNode->AddChild( node( "footprint", UnescapeString( footprint ) ) );
201 
202  if( datasheet.size() )
203  aNode->AddChild( node( "datasheet", UnescapeString( datasheet ) ) );
204 
205  if( userFields.size() )
206  {
207  XNODE* xfields;
208  aNode->AddChild( xfields = node( "fields" ) );
209 
210  // non MANDATORY fields are output alphabetically
211  for( const std::pair<const wxString, wxString>& f : userFields )
212  {
213  XNODE* xfield = node( "field", UnescapeString( f.second ) );
214  xfield->AddAttribute( "name", UnescapeString( f.first ) );
215  xfields->AddChild( xfield );
216  }
217  }
218 }
219 
220 
222 {
223  XNODE* xcomps = node( "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];
236  m_schematic->SetCurrentSheet( sheet );
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( "comp" ) );
284 
285  xcomp->AddAttribute( "ref", symbol->GetRef( &sheet ) );
286  addSymbolFields( xcomp, symbol, &sheetList[ ii ] );
287 
288  XNODE* xlibsource;
289  xcomp->AddChild( xlibsource = node( "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( "lib", libName );
308 
309  // We only want the symbol name, not the full LIB_ID.
310  xlibsource->AddAttribute( "part", partName );
311 
312  xlibsource->AddAttribute( "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( "property" ) );
321  xproperty->AddAttribute( "name", fields[jj].GetCanonicalName() );
322  xproperty->AddAttribute( "value", fields[jj].GetText() );
323  }
324 
325  for( const SCH_FIELD& sheetField : sheet.Last()->GetFields() )
326  {
327  xcomp->AddChild( xproperty = node( "property" ) );
328  xproperty->AddAttribute( "name", sheetField.GetCanonicalName() );
329  xproperty->AddAttribute( "value", sheetField.GetText() );
330  }
331 
332  if( !symbol->GetIncludeInBom() )
333  {
334  xcomp->AddChild( xproperty = node( "property" ) );
335  xproperty->AddAttribute( "name", "exclude_from_bom" );
336  }
337 
338  if( !symbol->GetIncludeOnBoard() )
339  {
340  xcomp->AddChild( xproperty = node( "property" ) );
341  xproperty->AddAttribute( "name", "exclude_from_board" );
342  }
343 
344  XNODE* xsheetpath;
345  xcomp->AddChild( xsheetpath = node( "sheetpath" ) );
346 
347  xsheetpath->AddAttribute( "names", sheet.PathHumanReadable() );
348  xsheetpath->AddAttribute( "tstamps", sheet.PathAsString() );
349 
350  XNODE* xunits; // Node for extra units
351  xcomp->AddChild( xunits = node( "tstamps" ) );
352 
353  auto range = extra_units.equal_range( symbol );
354 
355  // Output a series of children with all UUIDs associated with the REFDES
356  for( auto it = range.first; it != range.second; ++it )
357  {
358  wxString uuid = ( *it )->m_Uuid.AsString();
359 
360  // Add a space between UUIDs, if not in KICAD mode (i.e.
361  // using wxXmlDocument::Save()). KICAD MODE has its own XNODE::Format function.
362  if( !( aCtl & GNL_OPT_KICAD ) ) // i.e. for .xml format
363  uuid += ' ';
364 
365  xunits->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, uuid ) );
366  }
367 
368  // Output the primary UUID
369  xunits->AddChild(
370  new XNODE( wxXML_TEXT_NODE, wxEmptyString, symbol->m_Uuid.AsString() ) );
371  }
372  }
373 
374  return xcomps;
375 }
376 
377 
379 {
380  SCH_SCREEN* screen;
381  XNODE* xdesign = node( "design" );
382  XNODE* xtitleBlock;
383  XNODE* xsheet;
384  XNODE* xcomment;
385  XNODE* xtextvar;
386  wxString sheetTxt;
387  wxFileName sourceFileName;
388 
389  // the root sheet is a special sheet, call it source
390  xdesign->AddChild( node( "source", m_schematic->GetFileName() ) );
391 
392  xdesign->AddChild( node( "date", DateAndTime() ) );
393 
394  // which Eeschema tool
395  xdesign->AddChild( node( "tool", wxString( "Eeschema " ) + GetBuildVersion() ) );
396 
397  const std::map<wxString, wxString>& properties = m_schematic->Prj().GetTextVars();
398 
399  for( const std::pair<const wxString, wxString>& prop : properties )
400  {
401  xdesign->AddChild( xtextvar = node( "textvar", prop.second ) );
402  xtextvar->AddAttribute( "name", prop.first );
403  }
404 
405  /*
406  * Export the sheets information
407  */
408  SCH_SHEET_LIST sheetList = m_schematic->GetSheets();
409 
410  for( unsigned i = 0; i < sheetList.size(); i++ )
411  {
412  screen = sheetList[i].LastScreen();
413 
414  xdesign->AddChild( xsheet = node( "sheet" ) );
415 
416  // get the string representation of the sheet index number.
417  // Note that sheet->GetIndex() is zero index base and we need to increment the
418  // number by one to make it human readable
419  sheetTxt.Printf( "%u", i + 1 );
420  xsheet->AddAttribute( "number", sheetTxt );
421  xsheet->AddAttribute( "name", sheetList[i].PathHumanReadable() );
422  xsheet->AddAttribute( "tstamps", sheetList[i].PathAsString() );
423 
424  TITLE_BLOCK tb = screen->GetTitleBlock();
425  PROJECT* prj = &m_schematic->Prj();
426 
427  xsheet->AddChild( xtitleBlock = node( "title_block" ) );
428 
429  xtitleBlock->AddChild( node( "title", ExpandTextVars( tb.GetTitle(), prj ) ) );
430  xtitleBlock->AddChild( node( "company", ExpandTextVars( tb.GetCompany(), prj ) ) );
431  xtitleBlock->AddChild( node( "rev", ExpandTextVars( tb.GetRevision(), prj ) ) );
432  xtitleBlock->AddChild( node( "date", ExpandTextVars( tb.GetDate(), prj ) ) );
433 
434  // We are going to remove the fileName directories.
435  sourceFileName = wxFileName( screen->GetFileName() );
436  xtitleBlock->AddChild( node( "source", sourceFileName.GetFullName() ) );
437 
438  xtitleBlock->AddChild( xcomment = node( "comment" ) );
439  xcomment->AddAttribute( "number", "1" );
440  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 0 ), prj ) );
441 
442  xtitleBlock->AddChild( xcomment = node( "comment" ) );
443  xcomment->AddAttribute( "number", "2" );
444  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 1 ), prj ) );
445 
446  xtitleBlock->AddChild( xcomment = node( "comment" ) );
447  xcomment->AddAttribute( "number", "3" );
448  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 2 ), prj ) );
449 
450  xtitleBlock->AddChild( xcomment = node( "comment" ) );
451  xcomment->AddAttribute( "number", "4" );
452  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 3 ), prj ) );
453 
454  xtitleBlock->AddChild( xcomment = node( "comment" ) );
455  xcomment->AddAttribute( "number", "5" );
456  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 4 ), prj ) );
457 
458  xtitleBlock->AddChild( xcomment = node( "comment" ) );
459  xcomment->AddAttribute( "number", "6" );
460  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 5 ), prj ) );
461 
462  xtitleBlock->AddChild( xcomment = node( "comment" ) );
463  xcomment->AddAttribute( "number", "7" );
464  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 6 ), prj ) );
465 
466  xtitleBlock->AddChild( xcomment = node( "comment" ) );
467  xcomment->AddAttribute( "number", "8" );
468  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 7 ), prj ) );
469 
470  xtitleBlock->AddChild( xcomment = node( "comment" ) );
471  xcomment->AddAttribute( "number", "9" );
472  xcomment->AddAttribute( "value", ExpandTextVars( tb.GetComment( 8 ), prj ) );
473  }
474 
475  return xdesign;
476 }
477 
478 
480 {
481  XNODE* xlibs = node( "libraries" ); // auto_ptr
482  SYMBOL_LIB_TABLE* symbolLibTable = m_schematic->Prj().SchSymbolLibTable();
483 
484  for( std::set<wxString>::iterator it = m_libraries.begin(); it!=m_libraries.end(); ++it )
485  {
486  wxString libNickname = *it;
487  XNODE* xlibrary;
488 
489  if( symbolLibTable->HasLibrary( libNickname ) )
490  {
491  xlibs->AddChild( xlibrary = node( "library" ) );
492  xlibrary->AddAttribute( "logical", libNickname );
493  xlibrary->AddChild( node( "uri", symbolLibTable->GetFullURI( libNickname ) ) );
494  }
495 
496  // @todo: add more fun stuff here
497  }
498 
499  return xlibs;
500 }
501 
502 
504 {
505  XNODE* xlibparts = node( "libparts" ); // auto_ptr
506 
507  LIB_PINS pinList;
508  std::vector<LIB_FIELD*> fieldList;
509 
510  m_libraries.clear();
511 
512  for( auto lcomp : m_libParts )
513  {
514  wxString libNickname = lcomp->GetLibId().GetLibNickname();;
515 
516  // The library nickname will be empty if the cache library is used.
517  if( !libNickname.IsEmpty() )
518  m_libraries.insert( libNickname ); // inserts symbol's library if unique
519 
520  XNODE* xlibpart;
521  xlibparts->AddChild( xlibpart = node( "libpart" ) );
522  xlibpart->AddAttribute( "lib", libNickname );
523  xlibpart->AddAttribute( "part", lcomp->GetName() );
524 
525  //----- show the important properties -------------------------
526  if( !lcomp->GetDescription().IsEmpty() )
527  xlibpart->AddChild( node( "description", lcomp->GetDescription() ) );
528 
529  if( !lcomp->GetDatasheetField().GetText().IsEmpty() )
530  xlibpart->AddChild( node( "docs", lcomp->GetDatasheetField().GetText() ) );
531 
532  // Write the footprint list
533  if( lcomp->GetFPFilters().GetCount() )
534  {
535  XNODE* xfootprints;
536  xlibpart->AddChild( xfootprints = node( "footprints" ) );
537 
538  for( unsigned i = 0; i < lcomp->GetFPFilters().GetCount(); ++i )
539  xfootprints->AddChild( node( "fp", lcomp->GetFPFilters()[i] ) );
540  }
541 
542  //----- show the fields here ----------------------------------
543  fieldList.clear();
544  lcomp->GetFields( fieldList );
545 
546  XNODE* xfields;
547  xlibpart->AddChild( xfields = node( "fields" ) );
548 
549  for( const LIB_FIELD* field : fieldList )
550  {
551  if( !field->GetText().IsEmpty() )
552  {
553  XNODE* xfield;
554  xfields->AddChild( xfield = node( "field", field->GetText() ) );
555  xfield->AddAttribute( "name", field->GetCanonicalName() );
556  }
557  }
558 
559  //----- show the pins here ------------------------------------
560  pinList.clear();
561  lcomp->GetPins( pinList, 0, 0 );
562 
563  /* we must erase redundant Pins references in pinList
564  * These redundant pins exist because some pins
565  * are found more than one time when a symbol has
566  * multiple parts per package or has 2 representations (DeMorgan conversion)
567  * For instance, a 74ls00 has DeMorgan conversion, with different pin shapes,
568  * and therefore each pin appears 2 times in the list.
569  * Common pins (VCC, GND) can also be found more than once.
570  */
571  sort( pinList.begin(), pinList.end(), sortPinsByNumber );
572  for( int ii = 0; ii < (int)pinList.size()-1; ii++ )
573  {
574  if( pinList[ii]->GetNumber() == pinList[ii+1]->GetNumber() )
575  { // 2 pins have the same number, remove the redundant pin at index i+1
576  pinList.erase(pinList.begin() + ii + 1);
577  ii--;
578  }
579  }
580 
581  if( pinList.size() )
582  {
583  XNODE* pins;
584 
585  xlibpart->AddChild( pins = node( "pins" ) );
586  for( unsigned i=0; i<pinList.size(); ++i )
587  {
588  XNODE* pin;
589 
590  pins->AddChild( pin = node( "pin" ) );
591  pin->AddAttribute( "num", pinList[i]->GetShownNumber() );
592  pin->AddAttribute( "name", pinList[i]->GetShownName() );
593  pin->AddAttribute( "type", pinList[i]->GetCanonicalElectricalTypeName() );
594 
595  // caution: construction work site here, drive slowly
596  }
597  }
598  }
599 
600  return xlibparts;
601 }
602 
603 
605 {
606  XNODE* xnets = node( "nets" ); // auto_ptr if exceptions ever get used.
607  wxString netCodeTxt;
608  wxString netName;
609  wxString ref;
610 
611  XNODE* xnet = nullptr;
612 
613  /* output:
614  <net code="123" name="/cfcard.sch/WAIT#">
615  <node ref="R23" pin="1"/>
616  <node ref="U18" pin="12"/>
617  </net>
618  */
619 
620  struct NET_NODE
621  {
622  NET_NODE( SCH_PIN* aPin, const SCH_SHEET_PATH& aSheet, bool aNoConnect ) :
623  m_Pin( aPin ),
624  m_Sheet( aSheet ),
625  m_NoConnect( aNoConnect )
626  {}
627 
628  SCH_PIN* m_Pin;
629  SCH_SHEET_PATH m_Sheet;
630  bool m_NoConnect;
631  };
632 
633  struct NET_RECORD
634  {
635  NET_RECORD( const wxString& aName ) :
636  m_Name( aName )
637  {};
638 
639  wxString m_Name;
640  std::vector<NET_NODE> m_Nodes;
641  };
642 
643  std::vector<NET_RECORD*> nets;
644 
645  for( const auto& it : m_schematic->ConnectionGraph()->GetNetMap() )
646  {
647  wxString net_name = it.first.first;
648  auto subgraphs = it.second;
649  NET_RECORD* net_record;
650 
651  if( subgraphs.empty() )
652  continue;
653 
654  nets.emplace_back( new NET_RECORD( net_name ) );
655  net_record = nets.back();
656 
657  for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
658  {
659  bool nc = subgraph->m_no_connect && subgraph->m_no_connect->Type() == SCH_NO_CONNECT_T;
660  const SCH_SHEET_PATH& sheet = subgraph->m_sheet;
661 
662  for( SCH_ITEM* item : subgraph->m_items )
663  {
664  if( item->Type() == SCH_PIN_T )
665  {
666  SCH_PIN* pin = static_cast<SCH_PIN*>( item );
667  SCH_SYMBOL* symbol = pin->GetParentSymbol();
668 
669  if( !symbol
670  || ( ( aCtl & GNL_OPT_BOM ) && !symbol->GetIncludeInBom() )
671  || ( ( aCtl & GNL_OPT_KICAD ) && !symbol->GetIncludeOnBoard() ) )
672  {
673  continue;
674  }
675 
676  net_record->m_Nodes.emplace_back( pin, sheet, nc );
677  }
678  }
679  }
680  }
681 
682  // Netlist ordering: Net name, then ref des, then pin name
683  std::sort( nets.begin(), nets.end(),
684  []( const NET_RECORD* a, const NET_RECORD*b )
685  {
686  return StrNumCmp( a->m_Name, b->m_Name ) < 0;
687  } );
688 
689  for( int i = 0; i < (int) nets.size(); ++i )
690  {
691  NET_RECORD* net_record = nets[i];
692  bool added = false;
693  XNODE* xnode;
694 
695  // Netlist ordering: Net name, then ref des, then pin name
696  std::sort( net_record->m_Nodes.begin(), net_record->m_Nodes.end(),
697  []( const NET_NODE& a, const NET_NODE& b )
698  {
699  wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
700  wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
701 
702  if( refA == refB )
703  return a.m_Pin->GetShownNumber() < b.m_Pin->GetShownNumber();
704 
705  return refA < refB;
706  } );
707 
708  // Some duplicates can exist, for example on multi-unit parts with duplicated
709  // pins across units. If the user connects the pins on each unit, they will
710  // appear on separate subgraphs. Remove those here:
711  net_record->m_Nodes.erase(
712  std::unique( net_record->m_Nodes.begin(), net_record->m_Nodes.end(),
713  []( const NET_NODE& a, const NET_NODE& b )
714  {
715  wxString refA = a.m_Pin->GetParentSymbol()->GetRef( &a.m_Sheet );
716  wxString refB = b.m_Pin->GetParentSymbol()->GetRef( &b.m_Sheet );
717 
718  return refA == refB
719  && a.m_Pin->GetShownNumber() == b.m_Pin->GetShownNumber();
720  } ),
721  net_record->m_Nodes.end() );
722 
723  for( const NET_NODE& netNode : net_record->m_Nodes )
724  {
725  wxString refText = netNode.m_Pin->GetParentSymbol()->GetRef( &netNode.m_Sheet );
726  wxString pinText = netNode.m_Pin->GetShownNumber();
727 
728  // Skip power symbols and virtual symbols
729  if( refText[0] == wxChar( '#' ) )
730  continue;
731 
732  if( !added )
733  {
734  netCodeTxt.Printf( "%d", i + 1 );
735 
736  xnets->AddChild( xnet = node( "net" ) );
737  xnet->AddAttribute( "code", netCodeTxt );
738  xnet->AddAttribute( "name", net_record->m_Name );
739 
740  added = true;
741  }
742 
743  xnet->AddChild( xnode = node( "node" ) );
744  xnode->AddAttribute( "ref", refText );
745  xnode->AddAttribute( "pin", pinText );
746 
747  wxString pinName = netNode.m_Pin->GetShownName();
748  wxString pinType = netNode.m_Pin->GetCanonicalElectricalTypeName();
749 
750  if( !pinName.IsEmpty() )
751  xnode->AddAttribute( "pinfunction", pinName );
752 
753  if( netNode.m_NoConnect )
754  pinType += "+no_connect";
755 
756  xnode->AddAttribute( "pintype", pinType );
757  }
758  }
759 
760  for( NET_RECORD* record : nets )
761  delete record;
762 
763  return xnets;
764 }
765 
766 
767 XNODE* NETLIST_EXPORTER_XML::node( const wxString& aName,
768  const wxString& aTextualContent /* = wxEmptyString*/ )
769 {
770  XNODE* n = new XNODE( wxXML_ELEMENT_NODE, aName );
771 
772  if( aTextualContent.Len() > 0 ) // excludes wxEmptyString, the parameter's default value
773  n->AddChild( new XNODE( wxXML_TEXT_NODE, wxEmptyString, aTextualContent ) );
774 
775  return n;
776 }
777 
778 
779 static bool sortPinsByNumber( LIB_PIN* aPin1, LIB_PIN* aPin2 )
780 {
781  // return "lhs < rhs"
782  return StrNumCmp( aPin1->GetShownNumber(), aPin2->GetShownNumber(), true ) < 0;
783 }
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:216
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
wxString GetShownNumber() const
Definition: lib_pin.h:117
virtual std::map< wxString, wxString > & GetTextVars() const
Definition: project.cpp:78
const wxString & GetFileName() const
Definition: sch_screen.h:145
const UTF8 & GetLibItemName() const
Definition: lib_id.h:104
virtual wxString GetFileName() const =0
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
Container for project specific data.
Definition: project.h:62
SCH_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: sch_symbol.cpp:675
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject)
Definition: common.cpp:64
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
const wxString & GetComment(int aIdx) const
Definition: title_block.h:107
int GetUnitCount() const
Return the number of units per package of the symbol.
Definition: sch_symbol.cpp:370
std::vector< LIB_PIN * > LIB_PINS
Helper for defining a list of pin object pointers.
Definition: lib_item.h:55
wxString PathHumanReadable(bool aUseShortRootName=true) const
Return the sheet path in a human readable form made from the sheet names.
Field object used in symbol libraries.
Definition: lib_field.h:59
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:711
XNODE * makeRoot(unsigned aCtl=GNL_ALL)
Build the entire document tree for the generic export.
bool GetIncludeInBom() const
Definition: sch_symbol.h:659
XNODE * makeLibParts()
Fill out an XML node with the unique library parts and returns it.
wxString AsString() const
Definition: kiid.cpp:218
const TITLE_BLOCK & GetTitleBlock() const
Definition: sch_screen.h:150
wxString PathAsString() const
Return the path of time stamps which do not changes even when editing sheet parameters.
virtual CONNECTION_GRAPH * ConnectionGraph() const =0
void Clear()
Erase the record.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const
Return the reference for the given sheet path.
Definition: sch_symbol.cpp:441
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition: title_block.h:40
A subgraph is a set of items that are electrically connected on a single sheet.
wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: sch_field.cpp:105
bool IsVoid() const
Definition: sch_field.cpp:325
name of datasheet
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 * node(const wxString &aName, const wxString &aTextualContent=wxEmptyString)
A convenience function that creates a new XNODE with an optional textual child.
virtual PROJECT & Prj() const =0
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
std::vector< SCH_FIELD > & GetFields()
Definition: sch_sheet.h:88
XNODE * makeListOfNets(unsigned aCtl)
Fill out an XML node with a list of nets and returns it.
wxString GetBuildVersion()
Get the full KiCad version string.
const wxString & GetRevision() const
Definition: title_block.h:86
static bool sortPinsByNumber(LIB_PIN *aPin1, LIB_PIN *aPin2)
wxString GetSchSymbolLibraryName() const
Definition: sch_symbol.cpp:248
wxString GetFullURI(const wxString &aLibNickname, bool aExpandEnvVars=true) const
Return the full URI of the library mapped to aLibNickname.
const wxString & GetCompany() const
Definition: title_block.h:96
int GetFieldCount() const
Return the number of fields in this symbol.
Definition: sch_symbol.h:421
const NET_MAP & GetNetMap() const
Definition for symbol library class.
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition: lib_id.h:90
#define GNL_ALL
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
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:573
wxString UnescapeString(const wxString &aSource)
virtual void SetCurrentSheet(const SCH_SHEET_PATH &aPath)=0
const KIID m_Uuid
Definition: eda_item.h:475
Hold an XML or S-expression element.
Definition: xnode.h:43
The first 4 are mandatory, and must be instantiated in SCH_COMPONENT and LIB_PART constructors.
void addSymbolFields(XNODE *aNode, SCH_SYMBOL *aSymbol, SCH_SHEET_PATH *aSheet)
Holder for multi-unit symbol fields.
std::set< wxString > m_libraries
const wxString & GetDate() const
Definition: title_block.h:76
XNODE * makeSymbols(unsigned aCtl)
Schematic symbol object.
Definition: sch_symbol.h:78
SCH_SCREEN * LastScreen()
bool WriteNetlist(const wxString &aOutFileName, unsigned aNetlistOptions) override
Write generic netlist to aOutFileName.
wxString GetName(bool aUseDefaultName=true) const
Return the field name.
Definition: sch_field.cpp:544
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:110
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
The common library.
bool GetIncludeOnBoard() const
Definition: sch_symbol.h:662
wxString GetDescription() const
Return information about the aliased parts.
Definition: sch_symbol.cpp:266
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:624
const wxString & GetTitle() const
Definition: title_block.h:63
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
int GetUnit() const
Definition: sch_symbol.h:195
std::set< LIB_SYMBOL *, LIB_SYMBOL_LESS_THAN > m_libParts
unique library symbols used. LIB_SYMBOL items are sorted by names
wxString DateAndTime()
virtual SCH_SHEET_LIST GetSheets() const =0
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:193
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
XNODE * makeLibraries()
Fill out an XML node with a list of used libraries and returns it.
bool UseLibIdLookup() const
Definition: sch_symbol.h:162
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
const LIB_ID & GetLibId() const
Definition: sch_symbol.h:147
XNODE * makeDesignHeader()
Fill out a project "design" header into an XML node.