LTspice importer fixes

- Correctly find the library paths for new and old LTspice versions
- Use case insensitive comparison (store element names as lower case in the map)
- Better search paths
- Allow user to copy contents of LTspice "lib" folder to the same folder as file being imported

Partially addresses https://gitlab.com/kicad/code/kicad/-/issues/15061
This commit is contained in:
Roberto Fernandez Bautista 2023-07-09 22:51:01 +02:00
parent 6539171459
commit dfe7051171
4 changed files with 78 additions and 56 deletions

View File

@ -66,7 +66,7 @@ class SCH_SHAPE;
class LTSPICE_SCH_PARSER class LTSPICE_SCH_PARSER
{ {
public: public:
explicit LTSPICE_SCH_PARSER( const wxString& aFilename, LTSPICE_SCHEMATIC* aLTSchematic ) : explicit LTSPICE_SCH_PARSER( LTSPICE_SCHEMATIC* aLTSchematic ) :
m_lt_schematic( aLTSchematic ), m_lt_schematic( aLTSchematic ),
m_powerSymbolIndex( 0 ) m_powerSymbolIndex( 0 )
{ } { }

View File

@ -90,14 +90,26 @@ SCH_SHEET* SCH_LTSPICE_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSche
wxFileName ltspiceDataDir( KIPLATFORM::ENV::GetUserDataPath(), wxEmptyString ); wxFileName ltspiceDataDir( KIPLATFORM::ENV::GetUserDataPath(), wxEmptyString );
ltspiceDataDir.RemoveLastDir(); // "kicad" ltspiceDataDir.RemoveLastDir(); // "kicad"
ltspiceDataDir.AppendDir( wxS( "LTspice" ) ); ltspiceDataDir.AppendDir( wxS( "LTspice" ) );
ltspiceDataDir.AppendDir( "lib" );
if( !ltspiceDataDir.DirExists() ) if( !ltspiceDataDir.DirExists() )
{ {
ltspiceDataDir = wxFileName( KIPLATFORM::ENV::GetDocumentsPath(), wxEmptyString ); // See if user has older version of LTspice installed (e.g. C:\Users\USERNAME\Documents\LTspiceXVII\lib
ltspiceDataDir.AppendDir( wxS( "LTspiceXVII" ) ); wxString foundFile = wxFindFirstFile( KIPLATFORM::ENV::GetDocumentsPath() + wxFileName::GetPathSeparator() + "LTspice*", wxDIR );
while( !foundFile.empty() )
{
ltspiceDataDir = wxFileName(foundFile, wxEmptyString);
ltspiceDataDir.AppendDir( "lib" );
if( ltspiceDataDir.DirExists() )
break;
foundFile = wxFindNextFile();
}
} }
LTSPICE_SCHEMATIC ascFile( aFileName, ltspiceDataDir, nullptr, nullptr ); LTSPICE_SCHEMATIC ascFile( aFileName, ltspiceDataDir, m_reporter, m_progressReporter );
ascFile.Load( aSchematic, rootSheet, aFileName ); ascFile.Load( aSchematic, rootSheet, aFileName );
aSchematic->CurrentSheet().UpdateAllScreenReferences(); aSchematic->CurrentSheet().UpdateAllScreenReferences();

View File

@ -43,7 +43,7 @@ void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
m_schematic = aSchematic; m_schematic = aSchematic;
std::queue<LTSPICE_FILE> ascFileQueue; std::queue<LTSPICE_FILE> ascFileQueue;
LTSPICE_FILE ascFileObject( aLibraryFileName, { 0, 0 } ); LTSPICE_FILE ascFileObject( aLibraryFileName.GetName(), { 0, 0 } );
ascFileObject.Screen = aRootSheet->GetScreen(); ascFileObject.Screen = aRootSheet->GetScreen();
@ -70,7 +70,7 @@ void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
SCH_SCREEN* screen = new SCH_SCREEN( m_schematic ); SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
// Reading the .asc file // Reading the .asc file
wxString fileIndex = ascFileQueue.front().Name.GetName() + "." + LtspiceSchematicExtension; wxString fileIndex = ascFileQueue.front().ElementName;
wxString ascFilePath = mapOfAscFiles[fileIndex]; wxString ascFilePath = mapOfAscFiles[fileIndex];
wxString buffer = SafeReadFile( ascFilePath, "r" ); wxString buffer = SafeReadFile( ascFilePath, "r" );
@ -87,12 +87,10 @@ void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
newSubSchematicElements[i].Screen = screen; newSubSchematicElements[i].Screen = screen;
wxString ascFileName = newSubSchematicElements[i].Name.GetName();
SCH_SHEET* sheet = new SCH_SHEET(); SCH_SHEET* sheet = new SCH_SHEET();
newSubSchematicElements[i].Sheet = sheet; newSubSchematicElements[i].Sheet = sheet;
ascSheetMap[newSubSchematicElements[i].ElementName] = sheet;
ascSheetMap[ascFileName] = sheet;
ascFileQueue.push( newSubSchematicElements[i] ); ascFileQueue.push( newSubSchematicElements[i] );
ascFiles.push_back( newSubSchematicElements[i] ); ascFiles.push_back( newSubSchematicElements[i] );
@ -106,9 +104,7 @@ void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
for( unsigned int i = 0; i < ascFiles.size(); i++ ) for( unsigned int i = 0; i < ascFiles.size(); i++ )
{ {
// Reading the .asc file // Reading the .asc file
wxString fileName = ascFiles[i].Name.GetName(); wxString buffer = SafeReadFile( mapOfAscFiles[ascFiles[i].ElementName], wxS( "r" ) );
wxString fileIndex = fileName + wxS( "." ) + LtspiceSchematicExtension;
wxString buffer = SafeReadFile( mapOfAscFiles[ fileIndex ], wxS( "r" ) );
// Getting the keywords to read // Getting the keywords to read
sourceFiles = GetSchematicElements( buffer ); sourceFiles = GetSchematicElements( buffer );
@ -118,7 +114,7 @@ void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
SCH_SHEET* curSheet; SCH_SHEET* curSheet;
SCH_SHEET_PATH curSheetPath; SCH_SHEET_PATH curSheetPath;
LTSPICE_SCH_PARSER parser( fileName, this ); LTSPICE_SCH_PARSER parser( this );
if( i > 0 ) if( i > 0 )
{ {
@ -128,11 +124,11 @@ void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
curSheet = ascFiles[i].Sheet; curSheet = ascFiles[i].Sheet;
std::map tempAsyMap = ReadAsyFiles( tempVector, mapOfAsyFiles ); std::map tempAsyMap = ReadAsyFiles( tempVector, mapOfAsyFiles );
wxString ascFileName = ascFiles[i].Name.GetName(); wxString ascFileName = ascFiles[i].ElementName;
LT_ASC dummyAsc; LT_ASC dummyAsc;
LT_SYMBOL tempSymbol = SymbolBuilder( ascFileName, tempAsyMap[ascFileName], dummyAsc ); LT_SYMBOL tempSymbol = SymbolBuilder( ascFileName, tempAsyMap[ascFileName], dummyAsc );
LIB_SYMBOL* tempLibSymbol = new LIB_SYMBOL( std::string( ascFiles[i].Name.GetName() ) ); LIB_SYMBOL* tempLibSymbol = new LIB_SYMBOL( ascFiles[i].ElementName );
parser.CreateSymbol( tempSymbol, tempLibSymbol ); parser.CreateSymbol( tempSymbol, tempLibSymbol );
BOX2I bbox = tempLibSymbol->GetBoundingBox(); BOX2I bbox = tempLibSymbol->GetBoundingBox();
@ -144,7 +140,7 @@ void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
SCH_FIELD& sheetNameField = curSheet->GetFields()[SHEETNAME]; SCH_FIELD& sheetNameField = curSheet->GetFields()[SHEETNAME];
SCH_FIELD& fileNameSheet = curSheet->GetFields()[SHEETFILENAME]; SCH_FIELD& fileNameSheet = curSheet->GetFields()[SHEETFILENAME];
wxString sheetName = wxString::Format( wxS( "%s-subsheet-%d" ), wxString sheetName = wxString::Format( wxS( "%s-subsheet-%d" ),
ascFiles[i].Name.GetName(), ascFiles[i].ElementName,
i ); i );
sheetNameField.SetText( sheetName ); sheetNameField.SetText( sheetName );
@ -173,7 +169,7 @@ void LTSPICE_SCHEMATIC::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
std::vector<wxString> subSchematicAsyFiles; std::vector<wxString> subSchematicAsyFiles;
for( const LTSPICE_FILE& ascFile : ascFiles ) for( const LTSPICE_FILE& ascFile : ascFiles )
subSchematicAsyFiles.push_back( ascFile.Name.GetName() ); subSchematicAsyFiles.push_back( ascFile.ElementName );
std::vector<LTSPICE_SCHEMATIC::LT_ASC> lt_ascs = StructureBuilder(); std::vector<LTSPICE_SCHEMATIC::LT_ASC> lt_ascs = StructureBuilder();
parser.Parse( &curSheetPath, lt_ascs, subSchematicAsyFiles ); parser.Parse( &curSheetPath, lt_ascs, subSchematicAsyFiles );
@ -187,9 +183,7 @@ void LTSPICE_SCHEMATIC::SubSchematicCheck( std::vector<LTSPICE_FILE>& aSchematic
{ {
for( const LTSPICE_FILE& it : aSchematicElementsArray ) for( const LTSPICE_FILE& it : aSchematicElementsArray )
{ {
wxString libAscFile = it.Name.GetName() + ".asc"; if( aMapOfAscFiles[it.ElementName] != "" )
if( aMapOfAscFiles[libAscFile] != "" )
aSubSchematicSet.push_back( it ); aSubSchematicSet.push_back( it );
} }
} }
@ -199,32 +193,51 @@ void LTSPICE_SCHEMATIC::GetAscAndAsyFilePaths( std::map<wxString, wxString>& aMa
std::map<wxString, wxString>& aMapOfAsyFiles, std::map<wxString, wxString>& aMapOfAsyFiles,
const wxFileName& parentFileName ) const wxFileName& parentFileName )
{ {
wxString ltSpiceFolder = m_ltspiceDataDir.GetFullPath(); // List of files to search (Give highest priority to files contained in same directory)
wxString cmpFolder = ltSpiceFolder + wxS( "lib/cmp/" ); std::vector<wxString> searchDirs;
wxString subFolder = ltSpiceFolder + wxS( "lib/sub/" ); searchDirs.push_back( parentFileName.GetPath() );
wxString symFolder = ltSpiceFolder + wxS( "lib/sym/" ); searchDirs.push_back( m_ltspiceDataDir.GetPathWithSep() + wxS( "sub" ) );
wxArrayString fileList; searchDirs.push_back( m_ltspiceDataDir.GetPathWithSep() + wxS( "sym" ) );
wxDir::GetAllFiles( ltSpiceFolder, &fileList ); for( const wxString& searchDir : searchDirs )
for( const wxString& filepath : fileList )
{ {
wxString relPath = filepath; wxArrayString fileList;
wxDir::GetAllFiles( searchDir, &fileList );
if( relPath.StartsWith( cmpFolder ) ) for( const wxString& filepath : fileList )
relPath = relPath.Mid( cmpFolder.length() ); {
else if( relPath.StartsWith( subFolder ) ) wxFileName path = filepath;
relPath = relPath.Mid( subFolder.length() ); path.MakeRelativeTo( searchDir );
else if( relPath.StartsWith( symFolder ) ) wxString elementName = ( path.GetPathWithSep() + path.GetName() ).Lower();
relPath = relPath.Mid( symFolder.length() ); wxString extension = path.GetExt().Lower();
if( filepath.EndsWith( wxS( ".asc" ) ) ) elementName.Replace( '\\', '/' );
aMapOfAscFiles.insert( { relPath, filepath } );
else if( filepath.EndsWith( wxS( ".asy" ) ) ) auto logToMap = [&]( std::map<wxString, wxString>& aMapToLogTo )
aMapOfAsyFiles.insert( { relPath, filepath } ); {
if( aMapToLogTo.count( elementName ) )
{
if( m_reporter )
{
m_reporter->Report( wxString::Format(
_( "File at '%s' was ignored. Using previously found "
"file at '%s' instead." ),
filepath, aMapToLogTo.at( elementName ) ) );
}
}
else
{
aMapToLogTo.insert( { elementName, filepath } );
}
};
if( extension == wxS( "asc" ) )
logToMap( aMapOfAscFiles );
else if( extension == wxS( "asy" ) )
logToMap( aMapOfAsyFiles );
}
} }
aMapOfAscFiles.insert( { parentFileName.GetFullName(), parentFileName.GetFullPath() } );
} }
@ -236,11 +249,10 @@ LTSPICE_SCHEMATIC::ReadAsyFiles( const std::vector<LTSPICE_FILE>& aSourceFiles,
for( const LTSPICE_FILE& source : aSourceFiles ) for( const LTSPICE_FILE& source : aSourceFiles )
{ {
wxString fileName = source.Name.GetFullPath(); wxString fileName = source.ElementName;
wxString fileFullName = fileName + wxS( ".asy" );
if( aAsyFileMap.count( fileFullName ) ) if( aAsyFileMap.count( fileName ) )
resultantMap[ fileName ] = SafeReadFile( aAsyFileMap.at( fileFullName ), wxS( "r" ) ); resultantMap[fileName] = SafeReadFile( aAsyFileMap.at( fileName ), wxS( "r" ) );
} }
return resultantMap; return resultantMap;
@ -258,17 +270,15 @@ std::vector<LTSPICE_FILE> LTSPICE_SCHEMATIC::GetSchematicElements( const wxStrin
if( !tokens.IsEmpty() && tokens[0].Upper() == wxS( "SYMBOL" ) ) if( !tokens.IsEmpty() && tokens[0].Upper() == wxS( "SYMBOL" ) )
{ {
wxString rawName( tokens[1] ); wxString elementName( tokens[1] );
long posX, posY; long posX, posY;
tokens[2].ToLong( &posX ); tokens[2].ToLong( &posX );
tokens[3].ToLong( &posY ); tokens[3].ToLong( &posY );
rawName.Replace( '\\', '/' ); elementName.Replace( '\\', '/' );
wxFileName symbolName( rawName ); LTSPICE_FILE asyFile( elementName, VECTOR2I( (int) posX, (int) posY ) );
LTSPICE_FILE asyFile( symbolName, VECTOR2I( (int) posX, (int) posY ) );
resultantArray.push_back( asyFile ); resultantArray.push_back( asyFile );
} }
@ -464,10 +474,10 @@ LTSPICE_SCHEMATIC::LT_SYMBOL LTSPICE_SCHEMATIC::SymbolBuilder( const wxString& a
{ {
const std::map<wxString, wxString>& asyFiles = m_fileCache[ wxS( "asyFiles" ) ]; const std::map<wxString, wxString>& asyFiles = m_fileCache[ wxS( "asyFiles" ) ];
if( !asyFiles.count( aAscFileName ) ) if( !asyFiles.count( aAscFileName.Lower() ) )
THROW_IO_ERROR( wxString::Format( _( "Symbol '%s.asy' not found" ), aAscFileName ) ); THROW_IO_ERROR( wxString::Format( _( "Symbol '%s.asy' not found" ), aAscFileName ) );
return SymbolBuilder( aAscFileName, asyFiles.at( aAscFileName ), aAscFile ); return SymbolBuilder( aAscFileName, asyFiles.at( aAscFileName.Lower() ), aAscFile );
} }
LTSPICE_SCHEMATIC::LT_SYMBOL LTSPICE_SCHEMATIC::SymbolBuilder( const wxString& aAscFileName, LTSPICE_SCHEMATIC::LT_SYMBOL LTSPICE_SCHEMATIC::SymbolBuilder( const wxString& aAscFileName,

View File

@ -32,15 +32,15 @@
struct LTSPICE_FILE struct LTSPICE_FILE
{ {
wxFileName Name; wxString ElementName;
VECTOR2I Offset; VECTOR2I Offset;
int ParentIndex; int ParentIndex;
SCH_SHEET* Sheet; SCH_SHEET* Sheet;
SCH_SCREEN* Screen; SCH_SCREEN* Screen;
SCH_SHEET_PATH SheetPath; SCH_SHEET_PATH SheetPath;
LTSPICE_FILE( const wxFileName& aFilename, const VECTOR2I& aOffset ) : LTSPICE_FILE( const wxString& aElementName, const VECTOR2I& aOffset ) :
Name( aFilename ), ElementName( aElementName.Lower() ),
Offset( aOffset ), Offset( aOffset ),
ParentIndex( 0 ), ParentIndex( 0 ),
Sheet( nullptr ), Sheet( nullptr ),
@ -49,7 +49,7 @@ struct LTSPICE_FILE
bool operator<( const LTSPICE_FILE& t ) const bool operator<( const LTSPICE_FILE& t ) const
{ {
return this->Name.GetFullName() < t.Name.GetFullName(); return ElementName < t.ElementName;
} }
}; };