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