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