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 (C) 2022-2023 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 along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
28#include <sch_screen.h>
29#include <wx/log.h>
30#include <wx/dir.h>
32#include <sch_sheet.h>
33#include <schematic.h>
34
35
36void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
37 const wxFileName& aLibraryFileName )
38{
39 std::map<wxString, wxString> mapOfAscFiles;
40 std::map<wxString, wxString> mapOfAsyFiles;
41
42 // Library paths to search (Give highest priority to files contained in same directory)
43 GetAscAndAsyFilePaths( aLibraryFileName.GetPath(), false, mapOfAscFiles, mapOfAsyFiles );
44
45 // TODO: Custom paths go here (non-recursive)
46
47 // Default LTspice libs
48 GetAscAndAsyFilePaths( m_ltspiceDataDir.GetPathWithSep() + wxS( "sub" ), true, mapOfAscFiles,
49 mapOfAsyFiles );
50
51 GetAscAndAsyFilePaths( m_ltspiceDataDir.GetPathWithSep() + wxS( "sym" ), true, mapOfAscFiles,
52 mapOfAsyFiles );
53
54 m_schematic = aSchematic;
55
56 std::queue<wxString> ascFileQueue;
57 ascFileQueue.push( aLibraryFileName.GetName().Lower() );
58
59 LTSPICE_FILE rootAscFile( ascFileQueue.front(), { 0, 0 } );
60
61 rootAscFile.Sheet = aRootSheet;
62 rootAscFile.Screen = new SCH_SCREEN();
63
64 int parentSheetIndex = 0;
65
66 // Asc files who are subschematic in nature
67 std::vector<LTSPICE_FILE> ascFiles;
68
69 ascFiles.push_back( rootAscFile );
70
71 while( !ascFileQueue.empty() )
72 {
73 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
74
75 // Reading the .asc file
76 wxString ascFilePath = mapOfAscFiles[ ascFileQueue.front() ];
77 wxString buffer = SafeReadFile( ascFilePath, "r" );
78
79 std::vector<LTSPICE_FILE> newSubSchematicElements = GetSchematicElements( buffer );
80
81 alg::delete_if( newSubSchematicElements,
82 [&mapOfAscFiles]( const LTSPICE_FILE& ii )
83 {
84 return mapOfAscFiles[ii.ElementName].IsEmpty();
85 } );
86
87 for( LTSPICE_FILE& newSubSchematicElement : newSubSchematicElements )
88 {
89 wxString asyName = newSubSchematicElement.ElementName;
90 auto it = mapOfAsyFiles.find( asyName );
91
92 if( it == mapOfAsyFiles.end() )
93 continue;
94
95 wxString asyBuffer = SafeReadFile( it->second, "r" );
96
97 if( IsAsySubsheet( asyBuffer ) )
98 {
99 newSubSchematicElement.ParentIndex = parentSheetIndex;
100 newSubSchematicElement.Screen = screen;
101 newSubSchematicElement.Sheet = new SCH_SHEET();
102
103 ascFileQueue.push( newSubSchematicElement.ElementName );
104 ascFiles.push_back( newSubSchematicElement );
105 }
106 }
107
108 ascFileQueue.pop();
109
110 parentSheetIndex++;
111 }
112
113 for( unsigned int i = 0; i < ascFiles.size(); i++ )
114 {
115 // Reading the .asc file
116 wxString buffer = SafeReadFile( mapOfAscFiles[ascFiles[i].ElementName], wxS( "r" ) );
117
118 // Getting the keywords to read
119 std::vector<LTSPICE_FILE> sourceFiles = GetSchematicElements( buffer );
120
121 m_fileCache[ wxS( "asyFiles" ) ] = ReadAsyFiles( sourceFiles, mapOfAsyFiles );
122 m_fileCache[ wxS( "ascFiles" ) ][ wxS( "parentFile" ) ] = buffer;
123
124 SCH_SHEET* curSheet;
125 SCH_SHEET_PATH curSheetPath;
126 LTSPICE_SCH_PARSER parser( this );
127
128 if( i > 0 )
129 {
130 std::vector<LTSPICE_FILE> tempVector;
131
132 tempVector.push_back( ascFiles[i] );
133 curSheet = ascFiles[i].Sheet;
134
135 std::map tempAsyMap = ReadAsyFiles( tempVector, mapOfAsyFiles );
136 wxString ascFileName = ascFiles[i].ElementName;
137 LT_ASC dummyAsc;
138 LT_SYMBOL tempSymbol = SymbolBuilder( ascFileName, tempAsyMap[ascFileName], dummyAsc );
139 LIB_SYMBOL tempLibSymbol( ascFiles[i].ElementName );
140
141 parser.CreateSymbol( tempSymbol, &tempLibSymbol );
142
143 BOX2I bbox = tempLibSymbol.GetBoundingBox();
144
145 curSheet->SetSize( bbox.GetSize() );
146 curSheet->SetPosition( parser.ToKicadCoords( ascFiles[i].Offset ) + bbox.GetOrigin() );
147 curSheet->SetParent( ascFiles[ascFiles[i].ParentIndex].Sheet );
148
149 SCH_FIELD& sheetNameField = curSheet->GetFields()[SHEETNAME];
150 SCH_FIELD& fileNameSheet = curSheet->GetFields()[SHEETFILENAME];
151 wxString sheetName = wxString::Format( wxS( "%s-subsheet-%d" ),
152 ascFiles[i].ElementName,
153 i );
154
155 sheetNameField.SetText( sheetName );
156 fileNameSheet.SetText( sheetName + ".kicad_sch" );
157
158 curSheet->SetScreen( ascFiles[i].Screen );
159
160 curSheetPath = ascFiles[ascFiles[i].ParentIndex].SheetPath;
161 curSheetPath.push_back( curSheet );
162
163 ascFiles[i].SheetPath = curSheetPath;
164
165 ascFiles[ascFiles[i].ParentIndex].Sheet->GetScreen()->Append( curSheet );
166
167 curSheet->GetScreen()->SetFileName( m_schematic->Prj().GetProjectPath() + sheetName
168 + ".kicad_sch" );
169 }
170 else
171 {
172 curSheet = ascFiles[i].Sheet;
173
174 ascFiles[i].SheetPath.push_back( curSheet );
175 curSheetPath = ascFiles[i].SheetPath;
176 }
177
178 std::vector<wxString> subSchematicAsyFiles;
179
180 for( const LTSPICE_FILE& ascFile : ascFiles )
181 subSchematicAsyFiles.push_back( ascFile.ElementName );
182
183 std::vector<LTSPICE_SCHEMATIC::LT_ASC> lt_ascs = StructureBuilder();
184 parser.Parse( &curSheetPath, lt_ascs, subSchematicAsyFiles );
185 }
186}
187
188
189void LTSPICE_SCHEMATIC::GetAscAndAsyFilePaths( const wxDir& aDir, bool aRecursive,
190 std::map<wxString, wxString>& aMapOfAscFiles,
191 std::map<wxString, wxString>& aMapOfAsyFiles,
192 const wxString& aBase )
193{
194 wxString filename;
195
196 {
197 bool cont = aDir.GetFirst( &filename, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN );
198
199 while( cont )
200 {
201 wxFileName path( aDir.GetName(), filename );
202
203 auto logToMap = [&]( std::map<wxString, wxString>& aMapToLogTo, const wxString& aKey )
204 {
205 if( aMapToLogTo.count( aKey ) )
206 {
207 if( m_reporter )
208 {
209 m_reporter->Report( wxString::Format(
210 _( "File at '%s' was ignored. Using previously found "
211 "file at '%s' instead." ),
212 path.GetFullPath(), aMapToLogTo.at( aKey ) ) );
213 }
214 }
215 else
216 {
217 aMapToLogTo.emplace( aKey, path.GetFullPath() );
218 }
219 };
220
221 wxString elementName1 = ( aBase + path.GetName() ).Lower();
222 wxString elementName2 = path.GetName().Lower();
223 wxString extension = path.GetExt().Lower();
224
225 if( extension == wxS( "asc" ) )
226 {
227 logToMap( aMapOfAscFiles, elementName1 );
228
229 if( !aBase.IsEmpty() )
230 logToMap( aMapOfAscFiles, elementName2 );
231 }
232 else if( extension == wxS( "asy" ) )
233 {
234 logToMap( aMapOfAsyFiles, elementName1 );
235
236 if( !aBase.IsEmpty() )
237 logToMap( aMapOfAsyFiles, elementName2 );
238 }
239
240 cont = aDir.GetNext( &filename );
241 }
242 }
243
244 if( aRecursive )
245 {
246 bool cont = aDir.GetFirst( &filename, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN );
247
248 while( cont )
249 {
250 wxFileName path( aDir.GetName(), filename );
251 wxDir subDir( path.GetFullPath() );
252
253 GetAscAndAsyFilePaths( subDir, true, aMapOfAscFiles, aMapOfAsyFiles,
254 filename + wxS( "/" ) );
255
256 cont = aDir.GetNext( &filename );
257 }
258 }
259}
260
261
262std::map<wxString, wxString>
263LTSPICE_SCHEMATIC::ReadAsyFiles( const std::vector<LTSPICE_FILE>& aSourceFiles,
264 const std::map<wxString, wxString>& aAsyFileMap )
265{
266 std::map<wxString, wxString> resultantMap;
267
268 for( const LTSPICE_FILE& source : aSourceFiles )
269 {
270 wxString fileName = source.ElementName;
271
272 if( aAsyFileMap.count( fileName ) )
273 resultantMap[fileName] = SafeReadFile( aAsyFileMap.at( fileName ), wxS( "r" ) );
274 }
275
276 return resultantMap;
277}
278
279
280std::vector<LTSPICE_FILE> LTSPICE_SCHEMATIC::GetSchematicElements( const wxString& aAscFile )
281{
282 std::vector<LTSPICE_FILE> resultantArray;
283 wxArrayString lines = wxSplit( aAscFile, '\n' );
284
285 for( const wxString& line : lines )
286 {
287 wxArrayString tokens = wxSplit( line, ' ' );
288
289 if( !tokens.IsEmpty() && tokens[0].Upper() == wxS( "SYMBOL" ) )
290 {
291 wxString elementName( tokens[1] );
292 long posX, posY;
293
294 tokens[2].ToLong( &posX );
295 tokens[3].ToLong( &posY );
296
297 elementName.Replace( '\\', '/' );
298
299 LTSPICE_FILE asyFile( elementName, VECTOR2I( (int) posX, (int) posY ) );
300
301 resultantArray.push_back( asyFile );
302 }
303 }
304
305 return resultantArray;
306}
307
308
309bool LTSPICE_SCHEMATIC::IsAsySubsheet( const wxString& aAsyFile )
310{
311 wxStringTokenizer lines( aAsyFile, "\n" );
312
313 while( lines.HasMoreTokens() )
314 {
315 wxStringTokenizer parts( lines.GetNextToken(), " " );
316
317 if( parts.GetNextToken().IsSameAs( wxS( "SYMATTR" ), false )
318 && parts.GetNextToken().IsSameAs( wxS( "Prefix" ), false ) )
319 {
320 return false;
321 }
322 }
323
324 return true;
325}
326
327
328int LTSPICE_SCHEMATIC::integerCheck( const wxString& aToken, int aLineNumber,
329 const wxString& aFileName )
330{
331 long result;
332
333 if( !aToken.ToLong( &result ) )
334 {
335 THROW_IO_ERROR( wxString::Format( _( "Expecting integer at line %d in file %s" ),
336 aLineNumber,
337 aFileName ) );
338 }
339
340 return (int) result;
341}
342
343
344VECTOR2I LTSPICE_SCHEMATIC::pointCheck( const wxString& aTokenX, const wxString& aTokenY,
345 int aLineNumber, const wxString& aFileName )
346{
347 return VECTOR2I( integerCheck( aTokenX, aLineNumber, aFileName ),
348 integerCheck( aTokenY, aLineNumber, aFileName ) );
349}
350
351
352void LTSPICE_SCHEMATIC::tokensSizeRangeCheck( size_t aActualSize, int aExpectedMin,
353 int aExpectedMax, int aLineNumber,
354 const wxString& aFileName )
355{
356 if( (int) aActualSize < aExpectedMin )
357 {
358 THROW_IO_ERROR( wxString::Format( _( "Expected data missing on line %d in file %s" ),
359 aLineNumber,
360 aFileName ) );
361 }
362 else if( (int) aActualSize > aExpectedMax )
363 {
364 THROW_IO_ERROR( wxString::Format( _( "Extra data found on line %d in file %s" ),
365 aLineNumber,
366 aFileName ) );
367 }
368}
369
370
371void LTSPICE_SCHEMATIC::aggregateAttributeValue( wxArrayString& aTokens, int aIndex )
372{
373 // Merges a value which is across multiple tokens into one token with spaces in between.
374 for( int i = aIndex + 1; i < (int) aTokens.GetCount(); i++ )
375 aTokens[ aIndex ] += " " + aTokens[i];
376}
377
378
380{
381 std::map<int, LINESTYLE> lineStyleMap;
382
383 lineStyleMap[0] = LINESTYLE::SOLID;
384 lineStyleMap[1] = LINESTYLE::DASH;
385 lineStyleMap[2] = LINESTYLE::DOT;
386 lineStyleMap[3] = LINESTYLE::DASHDOT;
387 lineStyleMap[4] = LINESTYLE::DASHDOTDOT;
388
389 if( lineStyleMap.find( aValue ) == lineStyleMap.end() )
390 THROW_IO_ERROR( _( "Expecting 0, 1, 2, 3 or 4" ) );
391
392 return lineStyleMap[ aValue ];
393}
394
395
397{
398 std::map<wxString, LINEWIDTH> lineWidthMap;
399
400 lineWidthMap["NORMAL"] = LINEWIDTH::Normal;
401 lineWidthMap["WIDE"] = LINEWIDTH::Wide;
402
403 if( lineWidthMap.find( aValue.Upper() ) == lineWidthMap.end() )
404 THROW_IO_ERROR( _( "Expecting NORMAL or WIDE" ) );
405
406 return lineWidthMap[ aValue.Upper() ];
407}
408
409
411{
412 std::map<wxString, POLARITY> polarityMap;
413
414 polarityMap["I"] = POLARITY::INPUT;
415 polarityMap["O"] = POLARITY::OUTPUT;
416 polarityMap["B"] = POLARITY::BIDIR;
417 polarityMap["IN"] = POLARITY::INPUT;
418 polarityMap["OUT"] = POLARITY::OUTPUT;
419 polarityMap["BIDIR"] = POLARITY::BIDIR;
420
421 if( polarityMap.find( aValue.Upper() ) == polarityMap.end() )
422 THROW_IO_ERROR( _( "Expecting I, O, B, IN, OUT or BIDIR" ) );
423
424 return polarityMap[ aValue.Upper() ];
425}
426
427
429{
430 std::map<wxString, ORIENTATION> rotationMirrorMap;
431
432 rotationMirrorMap["R0"] = ORIENTATION::R0;
433 rotationMirrorMap["R90"] = ORIENTATION::R90;
434 rotationMirrorMap["R180"] = ORIENTATION::R180;
435 rotationMirrorMap["R270"] = ORIENTATION::R270;
436
437 rotationMirrorMap["M0"] = ORIENTATION::M0;
438 rotationMirrorMap["M90"] = ORIENTATION::M90;
439 rotationMirrorMap["M180"] = ORIENTATION::M180;
440 rotationMirrorMap["M270"] = ORIENTATION::M270;
441
442 if( rotationMirrorMap.find( aValue.Upper() ) == rotationMirrorMap.end() )
443 THROW_IO_ERROR( _( "Expecting R0, R90, R18, R270, M0, M90, M180 or M270" ) );
444
445 return rotationMirrorMap[ aValue.Upper() ];
446}
447
448
450{
451 std::map<wxString, JUSTIFICATION> justificationMap;
452
453 justificationMap["LEFT"] = JUSTIFICATION::LEFT;
454 justificationMap["CENTER"] = JUSTIFICATION::CENTER;
455 justificationMap["RIGHT"] = JUSTIFICATION::RIGHT;
456 justificationMap["VLEFT"] = JUSTIFICATION::VLEFT;
457 justificationMap["VRIGHT"] = JUSTIFICATION::VRIGHT;
458 justificationMap["VCENTER"] = JUSTIFICATION::VCENTER;
459 justificationMap["BOTTOM"] = JUSTIFICATION::BOTTOM;
460 justificationMap["TOP"] = JUSTIFICATION::TOP;
461 justificationMap["VBOTTOM"] = JUSTIFICATION::VBOTTOM;
462 justificationMap["VTOP"] = JUSTIFICATION::VTOP;
463
464 if( justificationMap.find( aValue.Upper() ) == justificationMap.end() )
465 THROW_IO_ERROR( _( "Expecting LEFT, CENTER, RIGHT, TOP, BOTTOM, VLEFT, VRIGHT, VCENTER, VTOP or VBOTTOM" ) );
466
467 return justificationMap[ aValue.Upper() ];
468}
469
470
472{
473 std::map<wxString, JUSTIFICATION> pinJustificationMap;
474
475 pinJustificationMap["BOTTOM"] = JUSTIFICATION::BOTTOM;
476 pinJustificationMap["NONE"] = JUSTIFICATION::NONE;
477 pinJustificationMap["LEFT"] = JUSTIFICATION::LEFT;
478 pinJustificationMap["RIGHT"] = JUSTIFICATION::RIGHT;
479 pinJustificationMap["TOP"] = JUSTIFICATION::TOP;
480 pinJustificationMap["VBOTTOM"] = JUSTIFICATION::VBOTTOM;
481 pinJustificationMap["VLEFT"] = JUSTIFICATION::VLEFT;
482 pinJustificationMap["VRIGHT"] = JUSTIFICATION::VRIGHT;
483 pinJustificationMap["VCENTER"] = JUSTIFICATION::VCENTER;
484 pinJustificationMap["VTOP"] = JUSTIFICATION::VTOP;
485
486 if( pinJustificationMap.find( aValue.Upper() ) == pinJustificationMap.end() )
487 THROW_IO_ERROR( _( "Expecting NONE, BOTTOM, TOP, LEFT, RIGHT, VBOTTOM, VTOP, VCENTER, VLEFT or VRIGHT" ) );
488
489 return pinJustificationMap[ aValue.Upper() ];
490}
491
492
494{
495 std::map<wxString, SYMBOLTYPE> symbolTypeMap;
496
497 symbolTypeMap["CELL"] = SYMBOLTYPE::CELL;
498 symbolTypeMap["BLOCK"] = SYMBOLTYPE::BLOCK;
499
500 if( symbolTypeMap.find( aValue.Upper() ) == symbolTypeMap.end() )
501 THROW_IO_ERROR( _( "Expecting CELL or BLOCK" ) );
502
503 return symbolTypeMap[ aValue.Upper() ];
504}
505
506
507void LTSPICE_SCHEMATIC::removeCarriageReturn( wxString& elementFromLine )
508{
509 if( elementFromLine.EndsWith( '\r' ) )
510 elementFromLine = elementFromLine.BeforeLast( '\r' );
511}
512
513
515 LT_ASC& aAscFile )
516{
517 const std::map<wxString, wxString>& asyFiles = m_fileCache[ wxS( "asyFiles" ) ];
518
519 if( !asyFiles.count( aAscFileName.Lower() ) )
520 THROW_IO_ERROR( wxString::Format( _( "Symbol '%s.asy' not found" ), aAscFileName ) );
521
522 return SymbolBuilder( aAscFileName, asyFiles.at( aAscFileName.Lower() ), aAscFile );
523}
524
526 const wxString& aAsyFileContent,
527 LT_ASC& aAscFile )
528{
529 LT_SYMBOL lt_symbol;
530 int lineNumber = 1;
531
532 lt_symbol.Name = aAscFileName;
535
536 for( wxString line : wxSplit( aAsyFileContent, '\n' ) )
537 {
538 removeCarriageReturn( line );
539
540 wxArrayString tokens = wxSplit( line, ' ' );
541
542 if( tokens.IsEmpty() )
543 continue;
544
545 wxString element = tokens[0].Upper();
546
547 if( element == "LINE" )
548 {
549 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, aAscFileName );
550
551 wxString lineWidth = tokens[1];
552 wxString startPointX = tokens[2];
553 wxString startPointY = tokens[3];
554 wxString endPointX = tokens[4];
555 wxString endPointY = tokens[5];
556
557 LINE lt_line;
558 lt_line.LineWidth = getLineWidth( lineWidth );
559 lt_line.Start = pointCheck( startPointX, startPointY, lineNumber, aAscFileName );
560 lt_line.End = pointCheck( endPointX, endPointY, lineNumber, aAscFileName );
561
562 int lineStyleNumber = 0; // default
563
564 if( tokens.size() == 7 )
565 {
566 wxString lineStyle = tokens[6];
567 lineStyleNumber = integerCheck( lineStyle, lineNumber, aAscFileName );
568 }
569
570 lt_line.LineStyle = getLineStyle( lineStyleNumber );
571
572 lt_symbol.Lines.push_back( lt_line );
573 }
574 else if( element == "RECTANGLE" )
575 {
576 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, aAscFileName );
577
578 wxString lineWidth = tokens[1];
579 wxString botRightX = tokens[2];
580 wxString botRightY = tokens[3];
581 wxString topLeftX = tokens[4];
582 wxString topRightY = tokens[5];
583
584 RECTANGLE rect;
585 rect.LineWidth = getLineWidth( lineWidth );
586 rect.BotRight = pointCheck( botRightX, botRightY, lineNumber, aAscFileName );
587 rect.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, aAscFileName );
588
589 int lineStyleNumber = 0; // default
590
591 if( tokens.size() == 7 )
592 {
593 wxString lineStyle = tokens[6];
594 lineStyleNumber = integerCheck( lineStyle, lineNumber, aAscFileName );
595 }
596
597 rect.LineStyle = getLineStyle( lineStyleNumber );
598
599 lt_symbol.Rectangles.push_back( rect );
600 }
601 else if( element == "CIRCLE" )
602 {
607 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, aAscFileName );
608
609 wxString lineWidth = tokens[1];
610 wxString botRightX = tokens[2];
611 wxString botRightY = tokens[3];
612 wxString topLeftX = tokens[4];
613 wxString topRightY = tokens[5];
614
615 CIRCLE circle;
616 circle.LineWidth = getLineWidth( lineWidth );
617 circle.BotRight = pointCheck( botRightX, botRightY, lineNumber, aAscFileName );
618 circle.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, aAscFileName );
619
620 int lineStyleNumber = 0; // default
621
622 if( tokens.size() == 7 )
623 {
624 wxString lineStyle = tokens[6];
625 lineStyleNumber = integerCheck( lineStyle, lineNumber, aAscFileName );
626 }
627
628 circle.LineStyle = getLineStyle( lineStyleNumber );
629
630 lt_symbol.Circles.push_back( circle );
631 }
632 else if( element == "ARC" )
633 {
639 tokensSizeRangeCheck( tokens.size(), 10, 11, lineNumber, aAscFileName );
640
641 wxString lineWidth = tokens[1];
642 wxString botRightX = tokens[2];
643 wxString botRightY = tokens[3];
644 wxString topLeftX = tokens[4];
645 wxString topRightY = tokens[5];
646 wxString arcStartPointX = tokens[6];
647 wxString arcStartPointY = tokens[7];
648 wxString arcEndPointX = tokens[8];
649 wxString arcEndPointY = tokens[9];
650
651 ARC arc;
652 arc.LineWidth = getLineWidth( lineWidth );
653 arc.BotRight = pointCheck( botRightX, botRightY, lineNumber, aAscFileName );
654 arc.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, aAscFileName );
655 arc.ArcStart = pointCheck( arcStartPointX, arcStartPointY, lineNumber, aAscFileName );
656 arc.ArcEnd = pointCheck( arcEndPointX, arcEndPointY, lineNumber, aAscFileName );
657
658 int lineStyleNumber = 0; // default
659
660 if( tokens.size() == 11 )
661 {
662 wxString lineStyle = tokens[10];
663 lineStyleNumber = integerCheck( lineStyle, lineNumber, aAscFileName );
664 }
665
666 arc.LineStyle = getLineStyle( lineStyleNumber );
667
668 lt_symbol.Arcs.push_back( arc );
669 }
670 else if( element == "WINDOW" )
671 {
672 tokensSizeRangeCheck( tokens.size(), 6, 6, lineNumber, aAscFileName );
673
674 wxString number = tokens[1];
675 wxString windowPosX = tokens[2];
676 wxString windowPosY = tokens[3];
677 wxString justification = tokens[4];
678 wxString fontSize = tokens[5];
679
680 LT_WINDOW window;
681 window.WindowNumber = integerCheck( number, lineNumber, aAscFileName );
682 window.Position = pointCheck( windowPosX, windowPosY, lineNumber, aAscFileName );
683 window.Justification = getTextJustification( justification );
684 window.FontSize = integerCheck( fontSize, lineNumber, aAscFileName );
685
686 // LTSpice appears to ignore hidden property from .asy files
687 if( window.FontSize == 0 )
688 window.FontSize = 2;
689
690 lt_symbol.Windows.push_back( window );
691 }
692 else if( element == "SYMATTR" )
693 {
694 aggregateAttributeValue( tokens, 2 );
695
696 tokensSizeRangeCheck( tokens.size(), 3, INT_MAX, lineNumber, aAscFileName );
697
698 wxString key = tokens[1];
699 wxString value = tokens[2];
700
701 lt_symbol.SymAttributes[ key.Capitalize() ] = value;
702 }
703 else if( element == "PIN" )
704 {
705 tokensSizeRangeCheck( tokens.size(), 5, 5, lineNumber, aAscFileName );
706
707 wxString pinLocationX = tokens[1];
708 wxString pinLocationY = tokens[2];
709 wxString Justification = tokens[3];
710 wxString nameOffSet = tokens[4];
711
712 LT_PIN pin;
713 pin.PinLocation = pointCheck( pinLocationX, pinLocationY, lineNumber,aAscFileName );
714 pin.PinJustification = getPinJustification( Justification );
715 pin.NameOffSet = integerCheck( nameOffSet, lineNumber, aAscFileName );
716
717 lt_symbol.Pins.push_back( pin );
718 }
719 else if( element == "PINATTR" )
720 {
721 aggregateAttributeValue( tokens, 2 );
722
723 tokensSizeRangeCheck( tokens.size(), 3, INT_MAX, lineNumber, aAscFileName );
724
725 wxString name = tokens[1];
726 wxString Value = tokens[2];
727
728 lt_symbol.Pins.back().PinAttribute.insert( { name, Value } );
729 }
730 else if( element == "SYMBOLTYPE" )
731 {
732 tokensSizeRangeCheck( tokens.size(), 2, 2, lineNumber, aAscFileName );
733
734 wxString symbolType = tokens[1];
735
736 lt_symbol.SymbolType = getSymbolType( symbolType );
737 }
738
739 lineNumber++;
740 }
741
742 return lt_symbol;
743}
744
745
746std::vector<LTSPICE_SCHEMATIC::LT_ASC> LTSPICE_SCHEMATIC::StructureBuilder()
747{
748 // Initialising Symbol Struct
749
750 std::vector<LT_ASC> ascFiles;
751
752 for( const auto& [ fileName, contents ] : m_fileCache[ wxS( "ascFiles" ) ] )
753 {
754 LT_ASC ascFile;
755 std::vector<LT_SYMBOL> symbolArray;
756
757 ascFile.SheetSize = VECTOR2I( 0, 0 );
758 ascFile.Symbols = symbolArray;
759 ascFile.Version = 0;
760 ascFile.SheetNumber = 0;
761
762 int lineNumber = 1;
763
764 for( wxString line : wxSplit( contents, '\n' ) )
765 {
766 removeCarriageReturn( line );
767
768 wxArrayString tokens = wxSplit( line, ' ' );
769
770 if( tokens.IsEmpty() )
771 continue;
772
773 wxString element = tokens[0].Upper();
774
775 if( element == "SHEET" )
776 {
777 tokensSizeRangeCheck( tokens.size(), 4, 4, lineNumber, fileName );
778
779 wxString sheetNumber = tokens[1];
780 wxString sheetWidth = tokens[2];
781 wxString sheetHeight = tokens[3];
782
783 ascFile.SheetNumber = integerCheck( sheetNumber, lineNumber, fileName );
784 ascFile.SheetSize = pointCheck( sheetWidth, sheetHeight, lineNumber, fileName );
785 }
786 else if( element == "SYMBOL" )
787 {
788 tokensSizeRangeCheck( tokens.size(), 5, 5, lineNumber, fileName );
789
790 wxString symbolName = tokens[1];
791 wxString posX = tokens[2];
792 wxString posY = tokens[3];
793 wxString rotate_mirror_option = tokens[4];
794
795 symbolName.Replace( '\\', '/' );
796
797 LT_SYMBOL lt_symbol = SymbolBuilder( symbolName, ascFile );
798 lt_symbol.Offset = pointCheck( posX, posY, lineNumber, fileName );
799 lt_symbol.SymbolOrientation = getSymbolRotationOrMirror( rotate_mirror_option );
800
801 ascFile.Symbols.push_back( lt_symbol );
802 ascFile.BoundingBox.Merge( lt_symbol.Offset );
803 }
804 else if( element == "WIRE" )
805 {
806 tokensSizeRangeCheck( tokens.size(), 5, 5, lineNumber, fileName );
807
808 wxString startPointX = tokens[1];
809 wxString startPointY = tokens[2];
810 wxString endPointX = tokens[3];
811 wxString endPointY = tokens[4];
812
813 WIRE wire;
814 wire.Start = pointCheck( startPointX, startPointY, lineNumber, fileName );
815 wire.End = pointCheck( endPointX, endPointY, lineNumber, fileName );
816
817 ascFile.Wires.push_back( wire );
818 ascFile.BoundingBox.Merge( wire.Start );
819 ascFile.BoundingBox.Merge( wire.End );
820 }
821 else if( element == "FLAG" )
822 {
823 tokensSizeRangeCheck( tokens.size(), 4, 4, lineNumber, fileName );
824
825 wxString posX = tokens[1];
826 wxString posY = tokens[2];
827 wxString name = tokens[3];
828
829 FLAG flag;
830 flag.Offset = pointCheck( posX, posY, lineNumber, fileName );
831 flag.Value = name;
832 flag.FontSize = 2;
833
834 ascFile.Flags.push_back( flag );
835 ascFile.BoundingBox.Merge( flag.Offset );
836 }
837 else if( element == "DATAFLAG" )
838 {
839 tokensSizeRangeCheck( tokens.size(), 4, 4, lineNumber, fileName );
840
841 wxString posX = tokens[1];
842 wxString posY = tokens[2];
843 wxString expression = tokens[3];
844
846 flag.Offset = pointCheck( posX, posY, lineNumber, fileName );
847 flag.Expression = expression;
848 flag.FontSize = 2;
849
850 ascFile.DataFlags.push_back( flag );
851 ascFile.BoundingBox.Merge( flag.Offset );
852 }
853 else if( element == "WINDOW" )
854 {
855 tokensSizeRangeCheck( tokens.size(), 6, 6, lineNumber, fileName );
856
857 wxString number = tokens[1];
858 wxString windowPosX = tokens[2];
859 wxString windowPosY = tokens[3];
860 wxString justification = tokens[4];
861 wxString fontSize = tokens[5];
862
863 int windowNumber = integerCheck( number, lineNumber, fileName );
864 LT_WINDOW* window = nullptr;
865
866 // Overwrite an existing window from the symbol definition
867 for( LT_WINDOW& candidate : ascFile.Symbols.back().Windows )
868 {
869 if( candidate.WindowNumber == windowNumber )
870 {
871 window = &candidate;
872 break;
873 }
874 }
875
876 if( !window )
877 {
878 ascFile.Symbols.back().Windows.emplace_back( LT_WINDOW() );
879 window = &ascFile.Symbols.back().Windows.back();
880 }
881
882 window->WindowNumber = windowNumber;
883 window->Position = pointCheck( windowPosX, windowPosY, lineNumber, fileName );
884 window->Justification = getTextJustification( justification );
885 window->FontSize = integerCheck( fontSize, lineNumber, fileName );
886
887 ascFile.BoundingBox.Merge( window->Position );
888 }
889 else if( element == "SYMATTR" )
890 {
891 tokensSizeRangeCheck( tokens.size(), 3, INT_MAX, lineNumber, fileName );
892
893 aggregateAttributeValue( tokens, 2 );
894
895 wxString name = tokens[1];
896 wxString value = tokens[2];
897
898 ascFile.Symbols.back().SymAttributes[ name.Upper() ] = value;
899 }
900 else if( element == "LINE" )
901 {
902 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, fileName );
903
904 wxString lineWidth = tokens[1];
905 wxString startPointX = tokens[2];
906 wxString startPointY = tokens[3];
907 wxString endPointX = tokens[4];
908 wxString endPointY = tokens[5];
909
910 LINE lt_line;
911 lt_line.LineWidth = getLineWidth( lineWidth );
912 lt_line.Start = pointCheck( startPointX, startPointY, lineNumber, fileName );
913 lt_line.End = pointCheck( endPointX, endPointY, lineNumber, fileName );
914
915 int lineStyleNumber = 0; // default
916
917 if( tokens.size() == 7 )
918 {
919 wxString lineStyle = tokens[6];
920 lineStyleNumber = integerCheck( lineStyle, lineNumber, fileName );
921 }
922
923 lt_line.LineStyle = getLineStyle( lineStyleNumber );
924
925 ascFile.Lines.push_back( lt_line );
926 ascFile.BoundingBox.Merge( lt_line.Start );
927 ascFile.BoundingBox.Merge( lt_line.End );
928 }
929 else if( element == "RECTANGLE" )
930 {
931 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, fileName );
932
933 wxString lineWidth = tokens[1];
934 wxString botRightX = tokens[2];
935 wxString botRightY = tokens[3];
936 wxString topLeftX = tokens[4];
937 wxString topRightY = tokens[5];
938
939 RECTANGLE rect;
940 rect.LineWidth = getLineWidth( tokens[1] );
941 rect.BotRight = pointCheck( botRightX, botRightY, lineNumber, fileName );
942 rect.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, fileName );
943
944 int lineStyleNumber = 0; // default
945
946 if( tokens.size() == 7 )
947 {
948 wxString lineStyle = tokens[6];
949 lineStyleNumber = integerCheck( lineStyle, lineNumber, fileName );
950 }
951
952 rect.LineStyle = getLineStyle( lineStyleNumber );
953
954 ascFile.Rectangles.push_back( rect );
955 ascFile.BoundingBox.Merge( rect.TopLeft );
956 ascFile.BoundingBox.Merge( rect.BotRight );
957 }
958 else if( element == "CIRCLE" )
959 {
960 tokensSizeRangeCheck( tokens.size(), 6, 7, lineNumber, fileName );
961
962 wxString lineWidth = tokens[1];
963 wxString botRightX = tokens[2];
964 wxString botRightY = tokens[3];
965 wxString topLeftX = tokens[4];
966 wxString topRightY = tokens[5];
967
968 CIRCLE circle;
969 circle.LineWidth = getLineWidth( lineWidth );
970 circle.BotRight = pointCheck( botRightX, botRightY, lineNumber, fileName );
971 circle.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, fileName );
972
973 int lineStyleNumber = 0; // default
974
975 if( tokens.size() == 7 )
976 {
977 wxString lineStyle = tokens[6];
978 lineStyleNumber = integerCheck( lineStyle, lineNumber, fileName );
979 }
980
981 circle.LineStyle = getLineStyle( lineStyleNumber );
982
983 ascFile.Circles.push_back( circle );
984 ascFile.BoundingBox.Merge( circle.TopLeft );
985 ascFile.BoundingBox.Merge( circle.BotRight );
986 }
987 else if( element == "ARC" )
988 {
994 tokensSizeRangeCheck( tokens.size(), 10, 11, lineNumber, fileName );
995
996 wxString lineWidth = tokens[1];
997 wxString botRightX = tokens[2];
998 wxString botRightY = tokens[3];
999 wxString topLeftX = tokens[4];
1000 wxString topRightY = tokens[5];
1001 wxString arcStartPointX = tokens[6];
1002 wxString arcStartPointY = tokens[7];
1003 wxString arcEndPointX = tokens[8];
1004 wxString arcEndPointY = tokens[9];
1005
1006 ARC arc;
1007 arc.LineWidth = getLineWidth( lineWidth );
1008 arc.BotRight = pointCheck( botRightX, botRightY, lineNumber, fileName );
1009 arc.TopLeft = pointCheck( topLeftX, topRightY, lineNumber, fileName );
1010 arc.ArcEnd = pointCheck( arcStartPointX, arcStartPointY, lineNumber, fileName );
1011 arc.ArcStart = pointCheck( arcEndPointX, arcEndPointY, lineNumber, fileName );
1012
1013 int lineStyleNumber = 0; // default
1014
1015 if( tokens.size() == 11 )
1016 {
1017 wxString lineStyle = tokens[10];
1018 lineStyleNumber = integerCheck( lineStyle, lineNumber, fileName );
1019 }
1020
1021 arc.LineStyle = getLineStyle( lineStyleNumber );
1022
1023 ascFile.Arcs.push_back( arc );
1024 ascFile.BoundingBox.Merge( arc.TopLeft );
1025 ascFile.BoundingBox.Merge( arc.BotRight );
1026 }
1027 else if( element == "IOPIN" )
1028 {
1029 tokensSizeRangeCheck( tokens.size(), 4, 4, lineNumber, fileName );
1030
1031 wxString pinLocationX = tokens[1];
1032 wxString pinLocationY = tokens[2];
1033 wxString pinPolarity = tokens[3];
1034
1035 IOPIN iopin;
1036 iopin.Location = pointCheck( pinLocationX, pinLocationY, lineNumber, fileName );
1037 iopin.Polarity = getPolarity( pinPolarity );
1038
1039 ascFile.Iopins.push_back( iopin );
1040 ascFile.BoundingBox.Merge( iopin.Location );
1041 }
1042 else if( element == "TEXT" )
1043 {
1044 aggregateAttributeValue( tokens, 5 );
1045
1046 tokensSizeRangeCheck( tokens.size(), 6, INT_MAX, lineNumber, fileName );
1047
1048 wxString positionX = tokens[1];
1049 wxString positionY = tokens[2];
1050 wxString justification = tokens[3];
1051 wxString fontSize = tokens[4];
1052 wxString value = tokens[5];
1053
1054 TEXT text;
1055 text.Offset = pointCheck( positionX, positionY, lineNumber, fileName );
1056 text.Justification = getTextJustification( justification );
1057 text.FontSize = integerCheck( fontSize, lineNumber, fileName );
1058
1059 if( value.StartsWith( wxS( "!" ), &text.Value ) )
1060 text.Value.Replace( wxS( "! " ), wxS( "\n" ) ); // replace subsequent ! with \n
1061 else if( value.StartsWith( wxS( ";" ), &text.Value ) )
1062 text.Value.Replace( wxS( "; " ), wxS( "\n" ) ); // replace subsequent ; with \n
1063 else
1064 text.Value = value;
1065
1066 text.Value.Replace( wxS( "\\n" ), wxS( "\n" ) );
1067
1068 ascFile.Texts.push_back( text );
1069 ascFile.BoundingBox.Merge( text.Offset );
1070 }
1071 else if( element == "BUSTAP" )
1072 {
1073 tokensSizeRangeCheck( tokens.size(), 5, 5, lineNumber, fileName );
1074
1075 wxString startPointX = tokens[1];
1076 wxString startPointY = tokens[2];
1077 wxString endPointX = tokens[3];
1078 wxString endPointY = tokens[4];
1079
1080 BUSTAP bustap;
1081 bustap.Start = pointCheck( startPointX, startPointY, lineNumber, fileName );
1082 bustap.End = pointCheck( endPointX, endPointY, lineNumber, fileName );
1083
1084 ascFile.Bustap.push_back( bustap );
1085 ascFile.BoundingBox.Merge( bustap.Start );
1086 ascFile.BoundingBox.Merge( bustap.End );
1087 }
1088 else if( element == "VERSION" )
1089 {
1090 wxString versionNumber = tokens[1];
1091 ascFile.Version = integerCheck( versionNumber, lineNumber, fileName );
1092 }
1093
1094 lineNumber++;
1095 }
1096
1097 ascFiles.push_back( ascFile );
1098 }
1099
1100 return ascFiles;
1101}
1102
1103
const char * name
Definition: DXF_plotter.cpp:57
const Vec & GetOrigin() const
Definition: box2.h:184
const Vec & GetSize() const
Definition: box2.h:180
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:589
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:100
Define a library symbol object.
Definition: lib_symbol.h:99
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: lib_symbol.h:264
static VECTOR2I pointCheck(const wxString &aTokenX, const wxString &aTokenY, int aLineNumber, const wxString &aFileName)
static SYMBOLTYPE getSymbolType(const wxString &aValue)
std::vector< LT_ASC > StructureBuilder()
Build Intermediate data structure.
std::vector< LTSPICE_FILE > GetSchematicElements(const wxString &aAscFile)
Used to get symbols list present in asc file.
LT_SYMBOL SymbolBuilder(const wxString &aAscFileName, LT_ASC &aAscFile)
POLARITY
Polarity enum represents polarity of pin.
JUSTIFICATION
Defines in what ways the PIN or TEXT can be justified.
void GetAscAndAsyFilePaths(const wxDir &aDir, bool aRecursive, std::map< wxString, wxString > &aMapOfAscFiles, std::map< wxString, wxString > &aMapOfAsyFiles, const wxString &aBase=wxEmptyString)
Used to get file path for Asc and Asy files.
static ORIENTATION getSymbolRotationOrMirror(const wxString &aValue)
bool IsAsySubsheet(const wxString &aAsyFile)
Check if the asy file content indicates that we need to load subsheet.
void Load(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxFileName &aLibraryFileName)
The main function responsible for loading the .asc and .asy files.
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.
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.
static int integerCheck(const wxString &aToken, int aLineNumber, const wxString &aFileName)
Used to check if the given token in integer or not.
wxFileName m_ltspiceDataDir
std::map< wxString, std::map< wxString, wxString > > m_fileCache
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 > 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)
The class is been used for loading the asc and asy files in intermediate data structure.
int ToKicadCoords(int aCoordinate)
Method converts ltspice coordinate(i.e scale) to kicad coordinates.
void Parse(SCH_SHEET_PATH *aSheet, std::vector< LTSPICE_SCHEMATIC::LT_ASC > &outLT_ASCs, const std::vector< wxString > &aAsyFileNames)
Function responsible for loading the .asc and .asy files in intermediate data structure.
void CreateSymbol(LTSPICE_SCHEMATIC::LT_SYMBOL &aLtSymbol, LIB_SYMBOL *aLibSymbol)
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:143
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
Holds all the data relating to one schematic.
Definition: schematic.h:75
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:90
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:52
void SetText(const wxString &aText) override
Definition: sch_field.cpp:982
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:117
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:57
void SetSize(const VECTOR2I &aSize)
Definition: sch_sheet.h:113
std::vector< SCH_FIELD > & GetFields()
Definition: sch_sheet.h:93
void SetPosition(const VECTOR2I &aPosition) override
Definition: sch_sheet.cpp:923
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:110
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Definition: sch_sheet.cpp:161
#define _(s)
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
void delete_if(_Container &__c, _Function &&__f)
Deletes all values from __c for which __f returns true.
Definition: kicad_algo.h:173
wxString SafeReadFile(const wxString &aFilePath, const wxString &aReadType)
Nominally opens a file and reads it into a string.
Definition: richio.cpp:110
@ SHEETNAME
Definition: sch_sheet.h:45
@ SHEETFILENAME
Definition: sch_sheet.h:46
SCH_SCREEN * Screen
SCH_SHEET * Sheet
wxString ElementName
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< TEXT > Texts
std::vector< DATAFLAG > DataFlags
std::vector< LINE > Lines
std::vector< WIRE > Wires
std::vector< RECTANGLE > Rectangles
std::vector< FLAG > Flags
A struct to hold SYMBOL definition.
std::map< wxString, wxString > SymAttributes
std::vector< RECTANGLE > Rectangles
std::vector< LT_WINDOW > Windows
std::vector< LT_PIN > Pins
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.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588
Definition of file extensions used in Kicad.