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