KiCad PCB EDA Suite
Loading...
Searching...
No Matches
ltspice_schematic.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) 2022 Chetan Subhash Shinde<[email protected]>
5 * Copyright (C) 2023 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * 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, see <https://www.gnu.org/licenses/>.
20 */
21
25
27
29#include <sch_screen.h>
30#include <wx/log.h>
31#include <wx/dir.h>
32#include <wx/tokenzr.h>
34#include <sch_sheet.h>
35#include <sch_sheet_pin.h>
36#include <schematic.h>
37#include <page_info.h>
38#include <project.h>
39#include <lib_id.h>
40#include <richio.h>
41#include <string_utils.h>
42#include <algorithm>
43
44
45// Files are found in this order: foo/amp.asy, then bar/amp.asy.
46// The map is then:
47// "foo/amp" -> foo/amp.asy
48// "amp" -> foo/amp.asy
49// "bar/amp" -> bar/amp.asy
50//
51// "foo/amp" returns "amp", because "amp" is foo/amp.asy.
52// "bar/amp" returns "bar/amp", because "amp" is foo/amp.asy, not bar/amp.asy.
53static wxString getShortestKey( const std::map<wxString, wxString>& aMap, const wxString& aName )
54{
55 wxString key = aName.Lower();
56 key.Replace( '\\', '/' );
57
58 if( aMap.contains( key ) )
59 {
60 while( key.Contains( '/' ) )
61 {
62 wxString shorter = key.AfterFirst( '/' );
63
64 if( !aMap.contains( shorter ) || aMap.at( shorter ) != aMap.at( key ) )
65 break;
66
67 key = shorter;
68 }
69
70 return key;
71 }
72 else
73 {
74 key = key.AfterLast( '/' );
75
76 if( aMap.contains( key ) )
77 return key;
78 }
79
80 return {};
81}
82
83
84wxString LTSPICE_SCHEMATIC::GetSchematicFileName( const wxString& aAscKey ) const
85{
86 wxString name = getShortestKey( m_ascNamesMap, aAscKey );
87
88 if( name.IsEmpty() )
89 name = aAscKey.Lower();
90
92
94}
95
96
97wxString LTSPICE_SCHEMATIC::GetSymbolName( const wxString& aSymbolName ) const
98{
99 wxString name = getShortestKey( m_asyNamesMap, aSymbolName );
100
101 return LIB_ID::FixIllegalChars( name, false ).wx_str();
102}
103
104
105void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet, const wxFileName& aLibraryFileName,
106 REPORTER* aReporter )
107{
108 m_ascFileCache.clear();
109 m_asyFileCache.clear();
110 m_ascNamesMap.clear();
111 m_asyNamesMap.clear();
112
113 // Library paths to search (Give highest priority to files contained in same directory)
114 GetAscAndAsyFilePaths( aLibraryFileName.GetPath(), false, m_ascNamesMap, m_asyNamesMap );
115
116 // TODO: Custom paths go here (non-recursive)
117
118 // Default LTspice libs
119 GetAscAndAsyFilePaths( m_ltspiceDataDir.GetPathWithSep() + wxS( "sub" ), true, m_ascNamesMap, m_asyNamesMap );
120 GetAscAndAsyFilePaths( m_ltspiceDataDir.GetPathWithSep() + wxS( "sym" ), true, m_ascNamesMap, m_asyNamesMap );
121
122 wxString rootElementName = aLibraryFileName.GetName().Lower();
123 std::vector<wxString> uniqueAscNameKeys;
124
125 std::function<void( const wxString& )> collectHierarchy;
126 collectHierarchy = [&]( const wxString& aAscNameKey )
127 {
128 uniqueAscNameKeys.push_back( aAscNameKey );
129
130 if( !m_ascNamesMap.contains( aAscNameKey ) )
131 {
132 aReporter->Report( wxString::Format( _( "Missing '%s' schematic file" ), aAscNameKey + wxS( ".asc" ) ),
134 return;
135 }
136
137 wxString buffer = SafeReadFile( m_ascNamesMap.at( aAscNameKey ), wxS( "r" ) );
138 m_ascFileCache[m_ascNamesMap.at( aAscNameKey )] = buffer;
139
140 std::vector<LTSPICE_FILE> newSubSchematicElements = GetSchematicElements( buffer );
141
142 for( LTSPICE_FILE& newSubSchematicElement : newSubSchematicElements )
143 {
144 wxString asyKey = getShortestKey( m_asyNamesMap, newSubSchematicElement.ElementName );
145
146 if( alg::contains( uniqueAscNameKeys, asyKey ) )
147 continue;
148
149 if( asyKey.IsEmpty() )
150 continue;
151
152 wxString asyBuffer = SafeReadFile( m_asyNamesMap.at( asyKey ), wxS( "r" ) );
153 m_asyFileCache[m_asyNamesMap.at( asyKey )] = asyBuffer;
154
155 if( IsAsySubsheet( asyBuffer ) )
156 collectHierarchy( asyKey );
157 }
158 };
159
160 collectHierarchy( rootElementName );
161
162 std::map<wxString, LTSPICE_SCHEMATIC::LT_ASC> parsedAscFiles = ParseAscFiles( m_ascFileCache );
163 std::map<wxString, SCH_SCREEN*> mapOfScreens;
164
165 // Create a SCH_SCREEN for each unique .asc file
166 for( const wxString& elementName : uniqueAscNameKeys )
167 {
168 SCH_SCREEN* screen;
169
170 if( elementName == rootElementName )
171 screen = aRootSheet->GetScreen();
172 else
173 screen = new SCH_SCREEN( aSchematic );
174
175 screen->SetFileName( aSchematic->Project().GetProjectPath() + GetSchematicFileName( elementName ) );
176
177 mapOfScreens[elementName] = screen;
178 }
179
180 // Create KiCad items for each .asc file
181 for( const wxString& elementName : uniqueAscNameKeys )
182 {
183 SCH_IO_LTSPICE_PARSER parser( this );
184
185 if( !m_ascNamesMap.contains( elementName ) )
186 continue;
187
188 if( !parsedAscFiles.contains( m_ascNamesMap[elementName] ) )
189 continue;
190
191 LTSPICE_SCHEMATIC::LT_ASC& ascFile = parsedAscFiles[m_ascNamesMap[elementName]];
192 SCH_SCREEN* screen = mapOfScreens[elementName];
193
194 const int grid = schIUScale.MilsToIU( 50 );
195 const int margin = grid * 10;
196 VECTOR2I pageSize;
197 BOX2I bbox;
198
199 bbox.Merge( parser.ToKicadCoords( ascFile.BoundingBox.GetOrigin() ) );
200 bbox.Merge( parser.ToKicadCoords( ascFile.BoundingBox.GetEnd() ) );
201
202 // Set smallest page size to fit all elements
205 {
206 PAGE_INFO pageInfo( sizeType, false );
207
208 screen->SetPageSettings( pageInfo );
209 pageSize = KiROUND( pageInfo.GetSizeIU( schIUScale.IU_PER_MILS ) );
210
211 if( bbox.GetWidth() + margin < pageSize.x && bbox.GetHeight() + margin < pageSize.y )
212 break;
213 }
214
215 VECTOR2I originOffset = ( ( pageSize / 2 ) - bbox.GetCenter() ) / grid * grid;
216
217 try
218 {
219 parser.CreateItems( ascFile, screen, uniqueAscNameKeys, mapOfScreens, originOffset );
220 }
221 catch( IO_ERROR& e )
222 {
223 aReporter->Report( e.What(), RPT_SEVERITY_ERROR );
224 }
225 }
226
227 // Fix label casing
228 std::set<SCH_SCREEN*> visitedScreens;
229
230 std::function<void( SCH_SCREEN*, std::map<wxString, wxString> )> fixLabelCase;
231 fixLabelCase = [&]( SCH_SCREEN* aScreen, std::map<wxString, wxString> aLabelMap )
232 {
233 // Collect additional special casing from labels on screen
234 for( SCH_ITEM* item : aScreen->Items() )
235 {
236 if( SCH_LABEL_BASE* lbl = dynamic_cast<SCH_LABEL_BASE*>( item ) )
237 {
238 wxString lower = lbl->GetText().Lower();
239
240 if( !aLabelMap.contains( lower ) && lbl->GetText() != lower )
241 aLabelMap[lower] = lbl->GetText();
242 }
243 }
244
245 // Apply special casing to all labels and recurse
246 for( SCH_ITEM* item : aScreen->Items() )
247 {
248 if( SCH_LABEL_BASE* lbl = dynamic_cast<SCH_LABEL_BASE*>( item ) )
249 {
250 wxString lower = lbl->GetText().Lower();
251
252 if( aLabelMap.contains( lower ) )
253 lbl->SetText( aLabelMap.at( lower ) );
254 }
255 else if( SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item ) )
256 {
257 SCH_SCREEN* sheetScreen = sheet->GetScreen();
258
259 if( visitedScreens.contains( sheetScreen ) )
260 continue;
261
262 visitedScreens.insert( sheetScreen );
263
264 std::map<wxString, wxString> childLabelMap;
265
266 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
267 childLabelMap[pin->GetText().Lower()] = pin->GetText();
268
269 fixLabelCase( sheetScreen, childLabelMap );
270 }
271 }
272 };
273
274 fixLabelCase( aRootSheet->GetScreen(), {} );
275
276 SCH_SHEET_LIST sheets = aSchematic->BuildUnorderedSheetList();
277
278 // Number sheets in LTspice InstName order (parents before children, X2 before X10).
279 std::sort( sheets.begin(), sheets.end(),
280 []( const SCH_SHEET_PATH& a, const SCH_SHEET_PATH& b )
281 {
282 size_t len = std::min( a.size(), b.size() );
283
284 for( size_t i = 0; i < len; ++i )
285 {
286 SCH_SHEET* sheetA = a.at( i );
287 SCH_SHEET* sheetB = b.at( i );
288
289 if( sheetA->m_Uuid == sheetB->m_Uuid )
290 continue;
291
292 const SCH_FIELD* fieldA = sheetA->GetField( SCH_IO_LTSPICE_PARSER::LT_INSTNAME_FIELD );
293 const SCH_FIELD* fieldB = sheetB->GetField( SCH_IO_LTSPICE_PARSER::LT_INSTNAME_FIELD );
294 int cmp = StrNumCmp( fieldA ? fieldA->GetText() : wxString(),
295 fieldB ? fieldB->GetText() : wxString(), true );
296
297 if( cmp != 0 )
298 return cmp < 0;
299
300 return sheetA->m_Uuid < sheetB->m_Uuid;
301 }
302
303 return a.size() < b.size();
304 } );
305
306 sheets.SetInitialPageNumbers();
307
308 sheets.SortByHierarchicalPageNumbers( true );
309 sheets.CheckForMissingSymbolInstances( aSchematic->Project().GetProjectName() );
310
311 // Update references of all symbols in the schematic to include instance path suffixes ("R1_X4_X1")
312 for( SCH_SHEET_PATH& path : sheets )
313 {
314 if( path.size() <= 1 )
315 continue;
316
317 wxString suffix;
318
319 for( size_t i = 1; i < path.size(); ++i )
320 {
321 suffix += wxS( "_" );
322
323 if( SCH_FIELD* instNameField = path.at( i )->GetField( SCH_IO_LTSPICE_PARSER::LT_INSTNAME_FIELD ) )
324 suffix += instNameField->GetText();
325 }
326
327 SCH_SCREEN* screen = path.LastScreen();
328
329 if( !screen )
330 continue;
331
332 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
333 {
334 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
335 wxString ref = symbol->GetRef( &path );
336
337 symbol->SetRef( &path, ref + suffix );
338 }
339 }
340
341 for( const SCH_SHEET_PATH& path : sheets )
342 path.UpdateAllScreenReferences();
343
344 // Fixing all junctions at the end
345 aSchematic->FixupJunctionsAfterImport();
346}
347
348
349void LTSPICE_SCHEMATIC::GetAscAndAsyFilePaths( const wxDir& aDir, bool aRecursive,
350 std::map<wxString, wxString>& aMapOfAscFiles,
351 std::map<wxString, wxString>& aMapOfAsyFiles,
352 const std::vector<wxString>& aBaseDirs )
353{
354 wxString filename;
355
356 if( !aDir.IsOpened() )
357 return;
358
359 bool cont = aDir.GetFirst( &filename, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN );
360
361 while( cont )
362 {
363 wxFileName path( aDir.GetName(), filename );
364
365 auto logToMap =
366 [&]( std::map<wxString, wxString>& aMapToLogTo, const wxString& aKey )
367 {
368 if( aMapToLogTo.count( aKey ) )
369 {
370 if( m_reporter )
371 {
372 m_reporter->Report( wxString::Format( _( "File at '%s' was ignored. Using previously "
373 "found file at '%s' instead." ),
374 path.GetFullPath(),
375 aMapToLogTo.at( aKey ) ), RPT_SEVERITY_WARNING );
376 }
377 }
378 else
379 {
380 aMapToLogTo.emplace( aKey, path.GetFullPath() );
381 }
382 };
383
384 // Add dir1/dir2/cmp, dir2/cmp, cmp
385 wxString extension = path.GetExt().Lower();
386
387 for( size_t i = 0; i < aBaseDirs.size() + 1; i++ )
388 {
389 wxString alias;
390
391 for( size_t j = i; j < aBaseDirs.size(); j++ )
392 alias << aBaseDirs[j].Lower() << '/';
393
394 alias << path.GetName().Lower();
395
396 if( extension == wxS( "asc" ) )
397 logToMap( aMapOfAscFiles, alias );
398 else if( extension == wxS( "asy" ) )
399 logToMap( aMapOfAsyFiles, alias );
400 }
401
402 cont = aDir.GetNext( &filename );
403 }
404
405 if( aRecursive )
406 {
407 cont = aDir.GetFirst( &filename, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN );
408
409 while( cont )
410 {
411 wxFileName path( aDir.GetName(), filename );
412 wxDir subDir( path.GetFullPath() );
413
414 std::vector<wxString> newBase = aBaseDirs;
415 newBase.push_back( filename );
416
417 GetAscAndAsyFilePaths( subDir, true, aMapOfAscFiles, aMapOfAsyFiles, newBase );
418
419 cont = aDir.GetNext( &filename );
420 }
421 }
422}
423
424
425std::map<wxString, wxString> LTSPICE_SCHEMATIC::ReadAsyFiles( const std::vector<LTSPICE_FILE>& aSourceFiles,
426 const std::map<wxString, wxString>& aAsyFileMap )
427{
428 std::map<wxString, wxString> resultantMap;
429
430 for( const LTSPICE_FILE& source : aSourceFiles )
431 {
432 wxString fileName = source.ElementName;
433 wxString mapKey = getShortestKey( aAsyFileMap, fileName );
434
435 if( !mapKey.IsEmpty() )
436 resultantMap[fileName] = SafeReadFile( aAsyFileMap.at( mapKey ), wxS( "r" ) );
437 }
438
439 return resultantMap;
440}
441
442
443std::vector<LTSPICE_FILE> LTSPICE_SCHEMATIC::GetSchematicElements( const wxString& aAscFile )
444{
445 std::vector<LTSPICE_FILE> resultantArray;
446 wxArrayString lines = wxSplit( aAscFile, '\n' );
447
448 for( int i = 0; i < (int) lines.GetCount(); i++ )
449 {
450 wxArrayString tokens = wxSplit( lines[i], ' ' );
451
452 if( tokens.size() >= 4 && tokens[0].Upper() == wxS( "SYMBOL" ) )
453 {
454 wxString elementName( tokens[1] );
455 long posX, posY;
456
457 tokens[2].ToLong( &posX );
458 tokens[3].ToLong( &posY );
459
460 elementName.Replace( '\\', '/' );
461
462 LTSPICE_FILE asyFile( elementName, VECTOR2I( (int) posX, (int) posY ), i + 1 );
463
464 resultantArray.push_back( asyFile );
465 }
466 }
467
468 return resultantArray;
469}
470
471
472bool LTSPICE_SCHEMATIC::IsAsySubsheet( const wxString& aAsyFile )
473{
474 wxStringTokenizer lines( aAsyFile, "\r\n", wxTOKEN_STRTOK );
475
476 while( lines.HasMoreTokens() )
477 {
478 if( lines.GetNextToken().IsSameAs( wxS( "SymbolType BLOCK" ), false ) )
479 return true;
480 }
481
482 return false;
483}
484
485
486int LTSPICE_SCHEMATIC::integerCheck( const wxString& aToken, int aLineNumber, const wxString& aFileName )
487{
488 long result;
489
490 if( !aToken.ToLong( &result ) )
491 THROW_IO_ERRORF( _( "Expecting integer at line %d in file %s" ), aLineNumber, aFileName );
492
493 return (int) result;
494}
495
496
497VECTOR2I LTSPICE_SCHEMATIC::pointCheck( const wxString& aTokenX, const wxString& aTokenY, int aLineNumber,
498 const wxString& aFileName )
499{
500 return VECTOR2I( integerCheck( aTokenX, aLineNumber, aFileName ),
501 integerCheck( aTokenY, aLineNumber, aFileName ) );
502}
503
504
505void LTSPICE_SCHEMATIC::tokensSizeRangeCheck( size_t aActualSize, int aExpectedMin, int aExpectedMax,
506 int aLineNumber, const wxString& aFileName )
507{
508 if( (int) aActualSize < aExpectedMin )
509 THROW_IO_ERRORF( _( "Expected data missing on line %d in file %s" ), aLineNumber, aFileName );
510 else if( (int) aActualSize > aExpectedMax )
511 THROW_IO_ERRORF( _( "Extra data found on line %d in file %s" ), aLineNumber, aFileName );
512}
513
514
515void LTSPICE_SCHEMATIC::aggregateAttributeValue( wxArrayString& aTokens, int aIndex )
516{
517 // Merges a value which is across multiple tokens into one token with spaces in between.
518 for( int i = aIndex + 1; i < (int) aTokens.GetCount(); i++ )
519 aTokens[ aIndex ] += " " + aTokens[i];
520}
521
522
524{
525 std::map<int, LINESTYLE> lineStyleMap;
526
527 lineStyleMap[0] = LINESTYLE::SOLID;
528 lineStyleMap[1] = LINESTYLE::DASH;
529 lineStyleMap[2] = LINESTYLE::DOT;
530 lineStyleMap[3] = LINESTYLE::DASHDOT;
531 lineStyleMap[4] = LINESTYLE::DASHDOTDOT;
532
533 if( lineStyleMap.find( aValue ) == lineStyleMap.end() )
534 THROW_IO_ERROR( _( "Expecting 0, 1, 2, 3 or 4" ) );
535
536 return lineStyleMap[ aValue ];
537}
538
539
541{
542 std::map<wxString, LINEWIDTH> lineWidthMap;
543
544 lineWidthMap["NORMAL"] = LINEWIDTH::Normal;
545 lineWidthMap["WIDE"] = LINEWIDTH::Wide;
546
547 if( lineWidthMap.find( aValue.Upper() ) == lineWidthMap.end() )
548 THROW_IO_ERROR( _( "Expecting NORMAL or WIDE" ) );
549
550 return lineWidthMap[ aValue.Upper() ];
551}
552
553
555{
556 std::map<wxString, POLARITY> polarityMap;
557
558 polarityMap["I"] = POLARITY::PIN_INPUT;
559 polarityMap["O"] = POLARITY::OUTPUT;
560 polarityMap["B"] = POLARITY::BIDIR;
561 polarityMap["IN"] = POLARITY::PIN_INPUT;
562 polarityMap["OUT"] = POLARITY::OUTPUT;
563 polarityMap["BIDIR"] = POLARITY::BIDIR;
564
565 if( polarityMap.find( aValue.Upper() ) == polarityMap.end() )
566 THROW_IO_ERROR( _( "Expecting I, O, B, IN, OUT or BIDIR" ) );
567
568 return polarityMap[ aValue.Upper() ];
569}
570
571
573{
574 std::map<wxString, ORIENTATION> rotationMirrorMap;
575
576 rotationMirrorMap["R0"] = ORIENTATION::R0;
577 rotationMirrorMap["R90"] = ORIENTATION::R90;
578 rotationMirrorMap["R180"] = ORIENTATION::R180;
579 rotationMirrorMap["R270"] = ORIENTATION::R270;
580
581 rotationMirrorMap["M0"] = ORIENTATION::M0;
582 rotationMirrorMap["M90"] = ORIENTATION::M90;
583 rotationMirrorMap["M180"] = ORIENTATION::M180;
584 rotationMirrorMap["M270"] = ORIENTATION::M270;
585
586 if( rotationMirrorMap.find( aValue.Upper() ) == rotationMirrorMap.end() )
587 THROW_IO_ERROR( _( "Expecting R0, R90, R18, R270, M0, M90, M180 or M270" ) );
588
589 return rotationMirrorMap[ aValue.Upper() ];
590}
591
592
594{
595 std::map<wxString, JUSTIFICATION> justificationMap;
596
597 justificationMap["LEFT"] = JUSTIFICATION::LEFT;
598 justificationMap["CENTER"] = JUSTIFICATION::CENTER;
599 justificationMap["RIGHT"] = JUSTIFICATION::RIGHT;
600 justificationMap["VLEFT"] = JUSTIFICATION::VLEFT;
601 justificationMap["VRIGHT"] = JUSTIFICATION::VRIGHT;
602 justificationMap["VCENTER"] = JUSTIFICATION::VCENTER;
603 justificationMap["BOTTOM"] = JUSTIFICATION::BOTTOM;
604 justificationMap["TOP"] = JUSTIFICATION::TOP;
605 justificationMap["VBOTTOM"] = JUSTIFICATION::VBOTTOM;
606 justificationMap["VTOP"] = JUSTIFICATION::VTOP;
607 justificationMap["INVISIBLE"] = JUSTIFICATION::INVISIBLE;
608
609 if( justificationMap.find( aValue.Upper() ) == justificationMap.end() )
610 THROW_IO_ERROR( _( "Expecting LEFT, CENTER, RIGHT, TOP, BOTTOM, VLEFT, VRIGHT, VCENTER, "
611 "VTOP, VBOTTOM or INVISIBLE" ) );
612
613 return justificationMap[ aValue.Upper() ];
614}
615
616
618{
619 std::map<wxString, JUSTIFICATION> pinJustificationMap;
620
621 pinJustificationMap["BOTTOM"] = JUSTIFICATION::BOTTOM;
622 pinJustificationMap["NONE"] = JUSTIFICATION::NONE;
623 pinJustificationMap["LEFT"] = JUSTIFICATION::LEFT;
624 pinJustificationMap["RIGHT"] = JUSTIFICATION::RIGHT;
625 pinJustificationMap["TOP"] = JUSTIFICATION::TOP;
626 pinJustificationMap["VBOTTOM"] = JUSTIFICATION::VBOTTOM;
627 pinJustificationMap["VLEFT"] = JUSTIFICATION::VLEFT;
628 pinJustificationMap["VRIGHT"] = JUSTIFICATION::VRIGHT;
629 pinJustificationMap["VCENTER"] = JUSTIFICATION::VCENTER;
630 pinJustificationMap["VTOP"] = JUSTIFICATION::VTOP;
631
632 if( pinJustificationMap.find( aValue.Upper() ) == pinJustificationMap.end() )
633 THROW_IO_ERROR( _( "Expecting NONE, BOTTOM, TOP, LEFT, RIGHT, VBOTTOM, VTOP, VCENTER, VLEFT or VRIGHT" ) );
634
635 return pinJustificationMap[ aValue.Upper() ];
636}
637
638
640{
641 std::map<wxString, SYMBOLTYPE> symbolTypeMap;
642
643 symbolTypeMap["CELL"] = SYMBOLTYPE::CELL;
644 symbolTypeMap["BLOCK"] = SYMBOLTYPE::BLOCK;
645
646 if( symbolTypeMap.find( aValue.Upper() ) == symbolTypeMap.end() )
647 THROW_IO_ERROR( _( "Expecting CELL or BLOCK" ) );
648
649 return symbolTypeMap[ aValue.Upper() ];
650}
651
652
653void LTSPICE_SCHEMATIC::removeCarriageReturn( wxString& elementFromLine )
654{
655 if( elementFromLine.EndsWith( '\r' ) )
656 elementFromLine = elementFromLine.BeforeLast( '\r' );
657}
658
659
661 const wxString& aAsyFileContent, LT_ASC& aAscFile )
662{
663 LT_SYMBOL lt_symbol;
664 int lineNumber = 1;
665
666 lt_symbol.Name = aSymbolName;
669
670 for( wxString line : wxSplit( aAsyFileContent, '\n' ) )
671 {
672 removeCarriageReturn( line );
673
674 wxArrayString tokens = wxSplit( line, ' ' );
675
676 if( tokens.IsEmpty() )
677 continue;
678
679 wxString element = tokens[0].Upper();
680
681 if( element == "LINE" )
682 {
683 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, aSymbolName );
684
685 wxString lineWidth = tokens[1];
686 wxString startPointX = tokens[2];
687 wxString startPointY = tokens[3];
688 wxString endPointX = tokens[4];
689 wxString endPointY = tokens[5];
690
691 LINE lt_line;
692 lt_line.LineWidth = getLineWidth( lineWidth );
693 lt_line.Start = pointCheck( startPointX, startPointY, lineNumber, aSymbolName );
694 lt_line.End = pointCheck( endPointX, endPointY, lineNumber, aSymbolName );
695
696 int lineStyleNumber = 0; // default
697
698 if( tokens.size() == 7 )
699 {
700 wxString lineStyle = tokens[6];
701 lineStyleNumber = integerCheck( lineStyle, lineNumber, aSymbolName );
702 }
703
704 lt_line.LineStyle = getLineStyle( lineStyleNumber );
705
706 lt_symbol.Lines.push_back( lt_line );
707 }
708 else if( element == "RECTANGLE" )
709 {
710 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, aSymbolName );
711
712 wxString lineWidth = tokens[1];
713 wxString botRightX = tokens[2];
714 wxString botRightY = tokens[3];
715 wxString topLeftX = tokens[4];
716 wxString topRightY = tokens[5];
717
718 RECTANGLE rect;
719 rect.LineWidth = getLineWidth( lineWidth );
720 rect.BotRight = pointCheck( botRightX, botRightY, lineNumber, aSymbolName );
721 rect.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, aSymbolName );
722
723 int lineStyleNumber = 0; // default
724
725 if( tokens.size() == 7 )
726 {
727 wxString lineStyle = tokens[6];
728 lineStyleNumber = integerCheck( lineStyle, lineNumber, aSymbolName );
729 }
730
731 rect.LineStyle = getLineStyle( lineStyleNumber );
732
733 lt_symbol.Rectangles.push_back( rect );
734 }
735 else if( element == "CIRCLE" )
736 {
741 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, aSymbolName );
742
743 wxString lineWidth = tokens[1];
744 wxString botRightX = tokens[2];
745 wxString botRightY = tokens[3];
746 wxString topLeftX = tokens[4];
747 wxString topRightY = tokens[5];
748
750 circle.LineWidth = getLineWidth( lineWidth );
751 circle.BotRight = pointCheck( botRightX, botRightY, lineNumber, aSymbolName );
752 circle.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, aSymbolName );
753
754 int lineStyleNumber = 0; // default
755
756 if( tokens.size() == 7 )
757 {
758 wxString lineStyle = tokens[6];
759 lineStyleNumber = integerCheck( lineStyle, lineNumber, aSymbolName );
760 }
761
762 circle.LineStyle = getLineStyle( lineStyleNumber );
763
764 lt_symbol.Circles.push_back( circle );
765 }
766 else if( element == "ARC" )
767 {
773 tokensSizeRangeCheck( tokens.size(), 10, 11, lineNumber, aSymbolName );
774
775 wxString lineWidth = tokens[1];
776 wxString botRightX = tokens[2];
777 wxString botRightY = tokens[3];
778 wxString topLeftX = tokens[4];
779 wxString topRightY = tokens[5];
780 wxString arcStartPointX = tokens[6];
781 wxString arcStartPointY = tokens[7];
782 wxString arcEndPointX = tokens[8];
783 wxString arcEndPointY = tokens[9];
784
785 ARC arc;
786 arc.LineWidth = getLineWidth( lineWidth );
787 arc.BotRight = pointCheck( botRightX, botRightY, lineNumber, aSymbolName );
788 arc.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, aSymbolName );
789 arc.ArcStart = pointCheck( arcStartPointX, arcStartPointY, lineNumber, aSymbolName );
790 arc.ArcEnd = pointCheck( arcEndPointX, arcEndPointY, lineNumber, aSymbolName );
791
792 int lineStyleNumber = 0; // default
793
794 if( tokens.size() == 11 )
795 {
796 wxString lineStyle = tokens[10];
797 lineStyleNumber = integerCheck( lineStyle, lineNumber, aSymbolName );
798 }
799
800 arc.LineStyle = getLineStyle( lineStyleNumber );
801
802 lt_symbol.Arcs.push_back( arc );
803 }
804 else if( element == "WINDOW" )
805 {
806 tokensSizeRangeCheck( tokens.size(), 6, 6, lineNumber, aSymbolName );
807
808 wxString number = tokens[1];
809 wxString windowPosX = tokens[2];
810 wxString windowPosY = tokens[3];
811 wxString justification = tokens[4];
812 wxString fontSize = tokens[5];
813
814 LT_WINDOW window;
815 window.WindowNumber = integerCheck( number, lineNumber, aSymbolName );
816 window.Position = pointCheck( windowPosX, windowPosY, lineNumber, aSymbolName );
817 window.Justification = getTextJustification( justification );
818 window.FontSize = integerCheck( fontSize, lineNumber, aSymbolName );
819
820 // LTSpice appears to ignore hidden property from .asy files
821 if( window.FontSize == 0 )
822 window.FontSize = 2;
823
824 lt_symbol.Windows.push_back( window );
825 }
826 else if( element == "TEXT" )
827 {
828 aggregateAttributeValue( tokens, 5 );
829
830 tokensSizeRangeCheck( tokens.size(), 6, INT_MAX, lineNumber, aSymbolName );
831
832 wxString positionX = tokens[1];
833 wxString positionY = tokens[2];
834 wxString justification = tokens[3];
835 wxString fontSize = tokens[4];
836 wxString value = tokens[5];
837
838 TEXT text;
839 text.Offset = pointCheck( positionX, positionY, lineNumber, aSymbolName );
840 text.Justification = getTextJustification( justification );
841 text.FontSize = integerCheck( fontSize, lineNumber, aSymbolName );
842 text.Value = value;
843
844 text.Value.Replace( wxS( "\\n" ), wxS( "\n" ) );
845
846 lt_symbol.Texts.push_back( text );
847 }
848 else if( element == "SYMATTR" )
849 {
850 aggregateAttributeValue( tokens, 2 );
851
852 tokensSizeRangeCheck( tokens.size(), 3, INT_MAX, lineNumber, aSymbolName );
853
854 wxString key = tokens[1];
855 wxString value = tokens[2];
856
857 if( value == wxS( "\"\"" ) )
858 value = wxEmptyString;
859
860 lt_symbol.SymAttributes[ key.Upper() ] = value;
861 }
862 else if( element == "PIN" )
863 {
864 tokensSizeRangeCheck( tokens.size(), 5, 5, lineNumber, aSymbolName );
865
866 wxString pinLocationX = tokens[1];
867 wxString pinLocationY = tokens[2];
868 wxString Justification = tokens[3];
869 wxString nameOffSet = tokens[4];
870
871 LT_PIN pin;
872 pin.PinLocation = pointCheck( pinLocationX, pinLocationY, lineNumber,aSymbolName );
873 pin.PinJustification = getPinJustification( Justification );
874 pin.NameOffSet = integerCheck( nameOffSet, lineNumber, aSymbolName );
875
876 lt_symbol.Pins.push_back( pin );
877 }
878 else if( element == "PINATTR" )
879 {
880 aggregateAttributeValue( tokens, 2 );
881
882 tokensSizeRangeCheck( tokens.size(), 3, INT_MAX, lineNumber, aSymbolName );
883
884 wxString name = tokens[1];
885 wxString Value = tokens[2];
886
887 lt_symbol.Pins.back().PinAttribute.insert( { name, Value } );
888 }
889 else if( element == "SYMBOLTYPE" )
890 {
891 tokensSizeRangeCheck( tokens.size(), 2, 2, lineNumber, aSymbolName );
892
893 wxString symbolType = tokens[1];
894
895 lt_symbol.SymbolType = getSymbolType( symbolType );
896 }
897
898 lineNumber++;
899 }
900
901 return lt_symbol;
902}
903
904
906{
907 LT_SYMBOL lt_symbol;
908
909 lt_symbol.Name = aAscFileName;
910 lt_symbol.AsyKey = aAscFileName.Lower();
913
914 RECTANGLE rect;
917 rect.BotRight = VECTOR2I( 50, 50 );
918 rect.TopLeft = VECTOR2I( -50, -50 );
919 lt_symbol.Rectangles.push_back( rect );
920
921 return lt_symbol;
922}
923
924
925std::map<wxString, LTSPICE_SCHEMATIC::LT_ASC>
926LTSPICE_SCHEMATIC::ParseAscFiles( const std::map<wxString, wxString>& aAscFilesContent )
927{
928 // Initialising Schematic Struct
929
930 std::map<wxString, LT_ASC> ascFiles;
931
932 for( const auto& [ fileName, contents ] : aAscFilesContent )
933 {
934 LT_ASC ascFile;
935 std::vector<LT_SYMBOL> symbolArray;
936
937 ascFile.SheetSize = VECTOR2I( 0, 0 );
938 ascFile.Symbols = symbolArray;
939 ascFile.Version = 0;
940 ascFile.VersionMinor = 0;
941 ascFile.SheetNumber = 0;
942
943 int lineNumber = 1;
944
945 for( wxString line : wxSplit( contents, '\n' ) )
946 {
947 removeCarriageReturn( line );
948
949 wxArrayString tokens = wxSplit( line, ' ' );
950
951 if( tokens.IsEmpty() )
952 continue;
953
954 wxString element = tokens[0].Upper();
955
956 if( element == "SHEET" )
957 {
958 tokensSizeRangeCheck( tokens.size(), 4, 4, lineNumber, fileName );
959
960 wxString sheetNumber = tokens[1];
961 wxString sheetWidth = tokens[2];
962 wxString sheetHeight = tokens[3];
963
964 ascFile.SheetNumber = integerCheck( sheetNumber, lineNumber, fileName );
965 ascFile.SheetSize = pointCheck( sheetWidth, sheetHeight, lineNumber, fileName );
966 }
967 else if( element == "SYMBOL" )
968 {
969 tokensSizeRangeCheck( tokens.size(), 5, 5, lineNumber, fileName );
970
971 wxString symbolName = tokens[1];
972 wxString posX = tokens[2];
973 wxString posY = tokens[3];
974 wxString rotate_mirror_option = tokens[4];
975
976 symbolName.Replace( '\\', '/' );
977
978 try
979 {
980 wxString key = getShortestKey( m_asyNamesMap, symbolName );
981
982 if( !m_asyNamesMap.contains( key ) )
983 {
984 wxString fullName = wxFileName( fileName ).GetFullName();
985
987 wxString::Format( _( "Missing symbol file '%s.asy', referenced from '%s' at line %d" ),
988 symbolName, fullName, lineNumber ) );
989 }
990
991 if( !m_asyFileCache.contains( m_asyNamesMap.at( key ) ) )
992 THROW_IO_ERROR( wxString::Format( _( "Unable to read '%s'" ), m_asyNamesMap.at( key ) ) );
993
994 LT_SYMBOL lt_symbol =
995 ParseSymbol( symbolName, m_asyFileCache.at( m_asyNamesMap.at( key ) ), ascFile );
996
997 lt_symbol.AsyKey = key;
998 lt_symbol.Offset = pointCheck( posX, posY, lineNumber, fileName );
999 lt_symbol.SymbolOrientation = getSymbolRotationOrMirror( rotate_mirror_option );
1000
1001 ascFile.Symbols.push_back( lt_symbol );
1002 ascFile.BoundingBox.Merge( lt_symbol.Offset );
1003 }
1004 catch( IO_ERROR& e )
1005 {
1006 if( m_reporter )
1007 m_reporter->Report( e.What(), RPT_SEVERITY_ERROR );
1008
1009 // Use dummy symbol
1010 LT_SYMBOL lt_symbol = MakeDummySymbol( symbolName );
1011 lt_symbol.Offset = pointCheck( posX, posY, lineNumber, fileName );
1012 lt_symbol.SymbolOrientation = getSymbolRotationOrMirror( rotate_mirror_option );
1013
1014 ascFile.Symbols.push_back( lt_symbol );
1015 ascFile.BoundingBox.Merge( lt_symbol.Offset );
1016 }
1017 }
1018 else if( element == "WIRE" )
1019 {
1020 tokensSizeRangeCheck( tokens.size(), 5, 5, lineNumber, fileName );
1021
1022 wxString startPointX = tokens[1];
1023 wxString startPointY = tokens[2];
1024 wxString endPointX = tokens[3];
1025 wxString endPointY = tokens[4];
1026
1027 WIRE wire;
1028 wire.Start = pointCheck( startPointX, startPointY, lineNumber, fileName );
1029 wire.End = pointCheck( endPointX, endPointY, lineNumber, fileName );
1030
1031 ascFile.Wires.push_back( wire );
1032 ascFile.BoundingBox.Merge( wire.Start );
1033 ascFile.BoundingBox.Merge( wire.End );
1034 }
1035 else if( element == "FLAG" )
1036 {
1037 tokensSizeRangeCheck( tokens.size(), 4, 4, lineNumber, fileName );
1038
1039 wxString posX = tokens[1];
1040 wxString posY = tokens[2];
1041 wxString name = tokens[3];
1042
1043 FLAG flag;
1044 flag.Offset = pointCheck( posX, posY, lineNumber, fileName );
1045 flag.Value = name;
1046 flag.FontSize = 2;
1047
1048 ascFile.Flags.push_back( flag );
1049 ascFile.BoundingBox.Merge( flag.Offset );
1050 }
1051 else if( element == "DATAFLAG" )
1052 {
1053 tokensSizeRangeCheck( tokens.size(), 4, 4, lineNumber, fileName );
1054
1055 wxString posX = tokens[1];
1056 wxString posY = tokens[2];
1057 wxString expression = tokens[3];
1058
1059 DATAFLAG flag;
1060 flag.Offset = pointCheck( posX, posY, lineNumber, fileName );
1061 flag.Expression = expression;
1062 flag.FontSize = 2;
1063
1064 ascFile.DataFlags.push_back( flag );
1065 ascFile.BoundingBox.Merge( flag.Offset );
1066 }
1067 else if( element == "WINDOW" )
1068 {
1069 tokensSizeRangeCheck( tokens.size(), 6, 6, lineNumber, fileName );
1070
1071 wxString number = tokens[1];
1072 wxString windowPosX = tokens[2];
1073 wxString windowPosY = tokens[3];
1074 wxString justification = tokens[4];
1075 wxString fontSize = tokens[5];
1076
1077 LT_SYMBOL* symbol = &ascFile.Symbols.back();
1078 LT_WINDOW* window = nullptr;
1079 int windowNumber = integerCheck( number, lineNumber, fileName );
1080
1081 // Overwrite an existing window from the symbol definition
1082 for( LT_WINDOW& candidate : symbol->Windows )
1083 {
1084 if( candidate.WindowNumber == windowNumber )
1085 {
1086 window = &candidate;
1087 break;
1088 }
1089 }
1090
1091 if( !window )
1092 {
1093 symbol->Windows.emplace_back( LT_WINDOW() );
1094 window = &symbol->Windows.back();
1095 }
1096
1097 window->WindowNumber = windowNumber;
1098 window->Position = pointCheck( windowPosX, windowPosY, lineNumber, fileName );
1099 window->Justification = getTextJustification( justification );
1100 window->FontSize = integerCheck( fontSize, lineNumber, fileName );
1101
1102 ascFile.BoundingBox.Merge( symbol->Offset + window->Position );
1103 }
1104 else if( element == "SYMATTR" )
1105 {
1106 tokensSizeRangeCheck( tokens.size(), 3, INT_MAX, lineNumber, fileName );
1107
1108 aggregateAttributeValue( tokens, 2 );
1109
1110 wxString name = tokens[1];
1111 wxString value = tokens[2];
1112
1113 if( value == wxS( "\"\"" ) )
1114 value = wxEmptyString;
1115
1116 ascFile.Symbols.back().SymAttributes[ name.Upper() ] = value;
1117 }
1118 else if( element == "LINE" )
1119 {
1120 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, fileName );
1121
1122 wxString lineWidth = tokens[1];
1123 wxString startPointX = tokens[2];
1124 wxString startPointY = tokens[3];
1125 wxString endPointX = tokens[4];
1126 wxString endPointY = tokens[5];
1127
1128 LINE lt_line;
1129 lt_line.LineWidth = getLineWidth( lineWidth );
1130 lt_line.Start = pointCheck( startPointX, startPointY, lineNumber, fileName );
1131 lt_line.End = pointCheck( endPointX, endPointY, lineNumber, fileName );
1132
1133 int lineStyleNumber = 0; // default
1134
1135 if( tokens.size() == 7 )
1136 {
1137 wxString lineStyle = tokens[6];
1138 lineStyleNumber = integerCheck( lineStyle, lineNumber, fileName );
1139 }
1140
1141 lt_line.LineStyle = getLineStyle( lineStyleNumber );
1142
1143 ascFile.Lines.push_back( lt_line );
1144 ascFile.BoundingBox.Merge( lt_line.Start );
1145 ascFile.BoundingBox.Merge( lt_line.End );
1146 }
1147 else if( element == "RECTANGLE" )
1148 {
1149 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, fileName );
1150
1151 // wxString lineWidth = tokens[1];
1152 wxString botRightX = tokens[2];
1153 wxString botRightY = tokens[3];
1154 wxString topLeftX = tokens[4];
1155 wxString topRightY = tokens[5];
1156
1157 RECTANGLE rect;
1158 rect.LineWidth = getLineWidth( tokens[1] );
1159 rect.BotRight = pointCheck( botRightX, botRightY, lineNumber, fileName );
1160 rect.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, fileName );
1161
1162 int lineStyleNumber = 0; // default
1163
1164 if( tokens.size() == 7 )
1165 {
1166 wxString lineStyle = tokens[6];
1167 lineStyleNumber = integerCheck( lineStyle, lineNumber, fileName );
1168 }
1169
1170 rect.LineStyle = getLineStyle( lineStyleNumber );
1171
1172 ascFile.Rectangles.push_back( rect );
1173 ascFile.BoundingBox.Merge( rect.TopLeft );
1174 ascFile.BoundingBox.Merge( rect.BotRight );
1175 }
1176 else if( element == "CIRCLE" )
1177 {
1178 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, fileName );
1179
1180 wxString lineWidth = tokens[1];
1181 wxString botRightX = tokens[2];
1182 wxString botRightY = tokens[3];
1183 wxString topLeftX = tokens[4];
1184 wxString topRightY = tokens[5];
1185
1186 CIRCLE circle;
1187 circle.LineWidth = getLineWidth( lineWidth );
1188 circle.BotRight = pointCheck( botRightX, botRightY, lineNumber, fileName );
1189 circle.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, fileName );
1190
1191 int lineStyleNumber = 0; // default
1192
1193 if( tokens.size() == 7 )
1194 {
1195 wxString lineStyle = tokens[6];
1196 lineStyleNumber = integerCheck( lineStyle, lineNumber, fileName );
1197 }
1198
1199 circle.LineStyle = getLineStyle( lineStyleNumber );
1200
1201 ascFile.Circles.push_back( circle );
1202 ascFile.BoundingBox.Merge( circle.TopLeft );
1203 ascFile.BoundingBox.Merge( circle.BotRight );
1204 }
1205 else if( element == "ARC" )
1206 {
1212 tokensSizeRangeCheck( tokens.size(), 10, 11, lineNumber, fileName );
1213
1214 wxString lineWidth = tokens[1];
1215 wxString botRightX = tokens[2];
1216 wxString botRightY = tokens[3];
1217 wxString topLeftX = tokens[4];
1218 wxString topRightY = tokens[5];
1219 wxString arcStartPointX = tokens[6];
1220 wxString arcStartPointY = tokens[7];
1221 wxString arcEndPointX = tokens[8];
1222 wxString arcEndPointY = tokens[9];
1223
1224 ARC arc;
1225 arc.LineWidth = getLineWidth( lineWidth );
1226 arc.BotRight = pointCheck( botRightX, botRightY, lineNumber, fileName );
1227 arc.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, fileName );
1228 arc.ArcEnd = pointCheck( arcStartPointX, arcStartPointY, lineNumber, fileName );
1229 arc.ArcStart = pointCheck( arcEndPointX, arcEndPointY, lineNumber, fileName );
1230
1231 int lineStyleNumber = 0; // default
1232
1233 if( tokens.size() == 11 )
1234 {
1235 wxString lineStyle = tokens[10];
1236 lineStyleNumber = integerCheck( lineStyle, lineNumber, fileName );
1237 }
1238
1239 arc.LineStyle = getLineStyle( lineStyleNumber );
1240
1241 ascFile.Arcs.push_back( arc );
1242 ascFile.BoundingBox.Merge( arc.TopLeft );
1243 ascFile.BoundingBox.Merge( arc.BotRight );
1244 }
1245 else if( element == "IOPIN" )
1246 {
1247 tokensSizeRangeCheck( tokens.size(), 4, 4, lineNumber, fileName );
1248
1249 wxString pinLocationX = tokens[1];
1250 wxString pinLocationY = tokens[2];
1251 wxString pinPolarity = tokens[3];
1252
1253 IOPIN iopin;
1254 iopin.Location = pointCheck( pinLocationX, pinLocationY, lineNumber, fileName );
1255 iopin.Polarity = getPolarity( pinPolarity );
1256
1257 ascFile.Iopins.push_back( iopin );
1258 ascFile.BoundingBox.Merge( iopin.Location );
1259 }
1260 else if( element == "TEXT" )
1261 {
1262 aggregateAttributeValue( tokens, 5 );
1263
1264 tokensSizeRangeCheck( tokens.size(), 6, INT_MAX, lineNumber, fileName );
1265
1266 wxString positionX = tokens[1];
1267 wxString positionY = tokens[2];
1268 wxString justification = tokens[3];
1269 wxString fontSize = tokens[4];
1270 wxString value = tokens[5];
1271
1272 TEXT text;
1273 text.Offset = pointCheck( positionX, positionY, lineNumber, fileName );
1274 text.Justification = getTextJustification( justification );
1275 text.FontSize = integerCheck( fontSize, lineNumber, fileName );
1276
1277 if( value.StartsWith( wxS( "!" ), &text.Value ) )
1278 text.Value.Replace( wxS( "! " ), wxS( "\n" ) ); // replace subsequent ! with \n
1279 else if( value.StartsWith( wxS( ";" ), &text.Value ) )
1280 text.Value.Replace( wxS( "; " ), wxS( "\n" ) ); // replace subsequent ; with \n
1281 else
1282 text.Value = value;
1283
1284 text.Value.Replace( wxS( "\\n" ), wxS( "\n" ) );
1285
1286 ascFile.Texts.push_back( text );
1287 ascFile.BoundingBox.Merge( text.Offset );
1288 }
1289 else if( element == "BUSTAP" )
1290 {
1291 tokensSizeRangeCheck( tokens.size(), 5, 5, lineNumber, fileName );
1292
1293 wxString startPointX = tokens[1];
1294 wxString startPointY = tokens[2];
1295 wxString endPointX = tokens[3];
1296 wxString endPointY = tokens[4];
1297
1298 BUSTAP bustap;
1299 bustap.Start = pointCheck( startPointX, startPointY, lineNumber, fileName );
1300 bustap.End = pointCheck( endPointX, endPointY, lineNumber, fileName );
1301
1302 ascFile.Bustap.push_back( bustap );
1303 ascFile.BoundingBox.Merge( bustap.Start );
1304 ascFile.BoundingBox.Merge( bustap.End );
1305 }
1306 else if( element == "VERSION" )
1307 {
1308 wxString versionNumber = tokens[1];
1309 if( versionNumber.Contains( '.' ) )
1310 {
1311 wxString majorStr = versionNumber.BeforeFirst( '.' );
1312 wxString minorStr = versionNumber.AfterFirst( '.' );
1313 ascFile.Version = integerCheck( majorStr, lineNumber, fileName );
1314 ascFile.VersionMinor = integerCheck( minorStr, lineNumber, fileName );
1315 }
1316 else
1317 {
1318 ascFile.Version = integerCheck( versionNumber, lineNumber, fileName );
1319 }
1320 }
1321
1322 lineNumber++;
1323 }
1324
1325 ascFiles[fileName] = std::move( ascFile );
1326 }
1327
1328 return ascFiles;
1329}
1330
1331
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr const Vec & GetOrigin() const
Definition box2.h:207
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:205
static VECTOR2I pointCheck(const wxString &aTokenX, const wxString &aTokenY, int aLineNumber, const wxString &aFileName)
LT_SYMBOL MakeDummySymbol(const wxString &aAscFileName)
static SYMBOLTYPE getSymbolType(const wxString &aValue)
LT_SYMBOL ParseSymbol(const wxString &aSymbolName, const wxString &aAsyFileContent, LT_ASC &aAscFile)
std::map< wxString, wxString > m_ascNamesMap
std::map< wxString, LT_ASC > ParseAscFiles(const std::map< wxString, wxString > &aAscFilesContent)
Build Intermediate data structure.
std::vector< LTSPICE_FILE > GetSchematicElements(const wxString &aAscFile)
Used to get symbols list present in asc file.
POLARITY
Polarity enum represents polarity of pin.
wxString GetSchematicFileName(const wxString &aAscKey) const
Return "amp.kicad_sch" for "foo/amp" when both keys are one .asc, else "foo_amp.kicad_sch".
wxString GetSymbolName(const wxString &aSymbolName) const
Return "amp" for "foo/amp" when both keys are one .asy, else "foo_amp".
JUSTIFICATION
Defines in what ways the PIN or TEXT can be justified.
static ORIENTATION getSymbolRotationOrMirror(const wxString &aValue)
bool IsAsySubsheet(const wxString &aAsyFile)
Check if the asy file content indicates that we need to load subsheet.
std::map< wxString, wxString > m_asyNamesMap
static POLARITY getPolarity(const wxString &aValue)
static void tokensSizeRangeCheck(size_t aActualSize, int aExpectedMin, int aExpectedMax, int aLineNumber, const wxString &aFileName)
Used to check size of the token generated from split function.
std::map< wxString, wxString > m_asyFileCache
static LINEWIDTH getLineWidth(const wxString &aValue)
ORIENTATION
Defines different types or rotation and mirror operation can be applied to a LT_SYMBOL.
static void aggregateAttributeValue(wxArrayString &aTokens, int aIndex)
Join value present across multiple tokens into one.
void GetAscAndAsyFilePaths(const wxDir &aDir, bool aRecursive, std::map< wxString, wxString > &aMapOfAscFiles, std::map< wxString, wxString > &aMapOfAsyFiles, const std::vector< wxString > &aBaseDirs={})
Used to get file path for Asc and Asy files.
void Load(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxFileName &aLibraryFileName, REPORTER *aReporter)
The main function responsible for loading the .asc and .asy files.
static int integerCheck(const wxString &aToken, int aLineNumber, const wxString &aFileName)
Used to check if the given token in integer or not.
static JUSTIFICATION getTextJustification(const wxString &aValue)
static JUSTIFICATION getPinJustification(const wxString &aValue)
SYMBOLTYPE
Type of Symbol loaded from asc and asy file.
std::map< wxString, wxString > m_ascFileCache
std::map< wxString, wxString > ReadAsyFiles(const std::vector< LTSPICE_FILE > &aSourceFiles, const std::map< wxString, wxString > &aAsyFileMap)
The function returns a map.
static void removeCarriageReturn(wxString &elementFromLine)
static LINESTYLE getLineStyle(int aValue)
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
const VECTOR2D GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition page_info.h:172
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:276
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
Holds all the data relating to one schematic.
Definition schematic.h:149
SCH_SHEET_LIST BuildUnorderedSheetList() const
PROJECT & Project() const
Return a reference to the project this schematic is part of.
Definition schematic.h:171
The class is been used for loading the asc and asy files in intermediate data structure.
static constexpr const wxChar * LT_INSTNAME_FIELD
int ToKicadCoords(int aCoordinate)
Method converts ltspice coordinate(i.e scale) to kicad coordinates.
void CreateItems(LTSPICE_SCHEMATIC::LT_ASC &aAscfile, SCH_SCREEN *aScreen, const std::vector< wxString > &aUniqueAscNames, std::map< wxString, SCH_SCREEN * > &aMapOfScreens, VECTOR2I aOriginOffset)
Create KiCad items based on LTspice data.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:145
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
void SetInitialPageNumbers()
Set initial sheet page numbers.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
size_t size() const
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:151
Schematic symbol object.
Definition sch_symbol.h:75
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
wxString wx_str() const
Definition utf8.cpp:41
#define _(s)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
static const std::string KiCadSchematicFileExtension
std::string source
static wxString getShortestKey(const std::map< wxString, wxString > &aMap, const wxString &aName)
Loads the asc file and asy files.
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:96
PAGE_SIZE_TYPE
Definition page_info.h:46
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
wxString SafeReadFile(const wxString &aFilePath, const wxString &aReadType)
Nominally opens a file and reads it into a string.
Definition richio.cpp:51
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
The ARC is represented inside a rectangle whose opposite site are given.
The CIRCLE is represented in Ltpsice inside a rectangle whose two opposite points and line style are ...
IOPIN is special contact on symbol used for IO operations.
A struct to hold .asc file definition.
std::vector< CIRCLE > Circles
std::vector< IOPIN > Iopins
std::vector< BUSTAP > Bustap
std::vector< LT_SYMBOL > Symbols
std::vector< DATAFLAG > DataFlags
std::vector< RECTANGLE > Rectangles
A struct to hold SYMBOL definition.
std::map< wxString, wxString > SymAttributes
std::vector< RECTANGLE > Rectangles
std::vector< LT_WINDOW > Windows
std::vector< CIRCLE > Circles
A 4-sided polygon with opposite equal sides, used in representing shapes.
A metallic connection, used for transfer, between two points or pin.
std::string path
KIBIS_PIN * pin
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
@ SCH_SYMBOL_T
Definition typeinfo.h:168
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
Definition of file extensions used in Kicad.