You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
12 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2022 Mikolaj Wielgus
  5. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 3
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * https://www.gnu.org/licenses/gpl-3.0.html
  20. * or you may search the http://www.gnu.org website for the version 3 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <filename_resolver.h>
  25. #include <pgm_base.h>
  26. #include <string>
  27. #include <string_utils.h>
  28. #include <common.h>
  29. #include <functional>
  30. #include <sch_symbol.h>
  31. #include <schematic.h>
  32. // Include simulator headers after wxWidgets headers to avoid conflicts with Windows headers
  33. // (especially on msys2 + wxWidgets 3.0.x)
  34. #include <sim/sim_lib_mgr.h>
  35. #include <sim/sim_library.h>
  36. #include <sim/sim_model.h>
  37. #include <sim/sim_model_ideal.h>
  38. using namespace std::placeholders;
  39. SIM_LIB_MGR::SIM_LIB_MGR( const PROJECT* aPrj ) :
  40. m_project( aPrj ),
  41. m_forceFullParse( false )
  42. {
  43. }
  44. void SIM_LIB_MGR::Clear()
  45. {
  46. m_libraries.clear();
  47. m_models.clear();
  48. }
  49. wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, REPORTER& aReporter )
  50. {
  51. FILENAME_RESOLVER resolver;
  52. resolver.SetProject( m_project );
  53. std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
  54. for( const EMBEDDED_FILES* embeddedFiles : m_embeddedFilesStack )
  55. embeddedFilesStack.push_back( embeddedFiles );
  56. wxString expandedPath = resolver.ResolvePath( aLibraryPath, wxEmptyString, embeddedFilesStack );
  57. wxFileName fn( expandedPath );
  58. if( fn.IsAbsolute() )
  59. return fn.GetFullPath();
  60. wxFileName projectFn( m_project ? m_project->AbsolutePath( expandedPath ) : expandedPath );
  61. if( projectFn.Exists() )
  62. return projectFn.GetFullPath();
  63. wxFileName spiceLibFn( expandedPath );
  64. wxString spiceLibDir;
  65. wxGetEnv( wxT( "SPICE_LIB_DIR" ), &spiceLibDir );
  66. if( !spiceLibDir.IsEmpty() && spiceLibFn.MakeAbsolute( spiceLibDir ) && spiceLibFn.Exists() )
  67. return spiceLibFn.GetFullPath();
  68. if( spiceLibDir.IsEmpty() || spiceLibFn.GetFullPath() == projectFn.GetFullPath() )
  69. {
  70. aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s'" ),
  71. projectFn.GetFullPath() ) );
  72. }
  73. else
  74. {
  75. aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s' or '%s'" ),
  76. projectFn.GetFullPath(),
  77. spiceLibFn.GetFullPath() ) );
  78. }
  79. return aLibraryPath;
  80. }
  81. wxString SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const wxString& aLibPath,
  82. const wxString& aRelativeLib,
  83. REPORTER& aReporter )
  84. {
  85. wxFileName testPath( aLibPath );
  86. wxString fullPath( aLibPath );
  87. if( !testPath.IsAbsolute() && !aRelativeLib.empty() )
  88. {
  89. wxString relLib( aRelativeLib );
  90. relLib = ResolveLibraryPath( relLib, aReporter );
  91. wxFileName fn( relLib );
  92. testPath.MakeAbsolute( fn.GetPath( true ) );
  93. fullPath = testPath.GetFullPath();
  94. }
  95. wxFileName fn( fullPath );
  96. if( !fn.Exists() )
  97. fullPath = aLibPath;
  98. fullPath = ResolveLibraryPath( fullPath, aReporter );
  99. return fullPath;
  100. }
  101. void SIM_LIB_MGR::SetLibrary( const wxString& aLibraryPath, REPORTER& aReporter )
  102. {
  103. wxString path = ResolveLibraryPath( aLibraryPath, aReporter );
  104. if( aReporter.HasMessageOfSeverity( RPT_SEVERITY_UNDEFINED | RPT_SEVERITY_ERROR ) )
  105. return;
  106. if( !wxFileName::Exists( path ) )
  107. {
  108. aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s'" ),
  109. path ) );
  110. return;
  111. }
  112. std::unique_ptr<SIM_LIBRARY> library = SIM_LIBRARY::Create( path, m_forceFullParse, aReporter,
  113. [&]( const wxString& libPath, const wxString& relativeLib ) -> wxString
  114. {
  115. return ResolveEmbeddedLibraryPath( libPath, relativeLib, aReporter );
  116. } );
  117. Clear();
  118. m_libraries[path] = std::move( library );
  119. }
  120. SIM_MODEL& SIM_LIB_MGR::CreateModel( SIM_MODEL::TYPE aType, const std::vector<SCH_PIN*>& aPins,
  121. REPORTER& aReporter )
  122. {
  123. m_models.push_back( SIM_MODEL::Create( aType, aPins, aReporter ) );
  124. return *m_models.back();
  125. }
  126. SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel,
  127. const std::vector<SCH_PIN*>& aPins, REPORTER& aReporter )
  128. {
  129. m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aReporter ) );
  130. return *m_models.back();
  131. }
  132. SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel, const std::vector<SCH_PIN*>& aPins,
  133. const std::vector<SCH_FIELD>& aFields, bool aResolve, int aDepth,
  134. REPORTER& aReporter )
  135. {
  136. m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aFields, aResolve, aDepth, aReporter ) );
  137. return *m_models.back();
  138. }
  139. SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const SCH_SHEET_PATH* aSheetPath, SCH_SYMBOL& aSymbol,
  140. bool aResolve, int aDepth, REPORTER& aReporter )
  141. {
  142. // Note: currently this creates a resolved model (all Kicad variables references are resolved
  143. // before building the model).
  144. //
  145. // That's not what we want if this is ever called from the Simulation Model Editor (or other
  146. // editors, but it is what we want if called to generate a netlist or other exported items.
  147. std::vector<SCH_FIELD> fields;
  148. for( const SCH_FIELD& field : aSymbol.GetFields() )
  149. {
  150. if( field.GetId() == FIELD_T::REFERENCE )
  151. {
  152. fields.emplace_back( &aSymbol, FIELD_T::USER, field.GetName() );
  153. fields.back().SetText( aSymbol.GetRef( aSheetPath ) );
  154. }
  155. else if( field.GetId() == FIELD_T::VALUE || field.GetName().StartsWith( wxS( "Sim." ) ) )
  156. {
  157. fields.emplace_back( &aSymbol, FIELD_T::USER, field.GetName() );
  158. fields.back().SetText( field.GetShownText( aSheetPath, false, aDepth ) );
  159. }
  160. }
  161. auto getOrCreateField =
  162. [&aSymbol, &fields]( const wxString& name ) -> SCH_FIELD*
  163. {
  164. for( SCH_FIELD& field : fields )
  165. {
  166. if( field.GetName().IsSameAs( name ) )
  167. return &field;
  168. }
  169. fields.emplace_back( &aSymbol, FIELD_T::USER, name );
  170. return &fields.back();
  171. };
  172. wxString deviceType;
  173. wxString modelType;
  174. wxString modelParams;
  175. wxString pinMap;
  176. bool storeInValue = false;
  177. // Infer RLC and VI models if they aren't specified
  178. if( SIM_MODEL::InferSimModel( aSymbol, &fields, aResolve, aDepth, SIM_VALUE_GRAMMAR::NOTATION::SI,
  179. &deviceType, &modelType, &modelParams, &pinMap ) )
  180. {
  181. getOrCreateField( SIM_DEVICE_FIELD )->SetText( deviceType );
  182. if( !modelType.IsEmpty() )
  183. getOrCreateField( SIM_DEVICE_SUBTYPE_FIELD )->SetText( modelType );
  184. getOrCreateField( SIM_PARAMS_FIELD )->SetText( modelParams );
  185. getOrCreateField( SIM_PINS_FIELD )->SetText( pinMap );
  186. storeInValue = true;
  187. }
  188. std::vector<SCH_PIN*> sourcePins = aSymbol.GetAllLibPins();
  189. std::sort( sourcePins.begin(), sourcePins.end(),
  190. []( const SCH_PIN* lhs, const SCH_PIN* rhs )
  191. {
  192. return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
  193. } );
  194. SIM_LIBRARY::MODEL model = CreateModel( fields, aResolve, aDepth, sourcePins, aReporter );
  195. model.model.SetIsStoredInValue( storeInValue );
  196. return model;
  197. }
  198. SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<SCH_FIELD>& aFields,
  199. bool aResolve, int aDepth,
  200. const std::vector<SCH_PIN*>& aPins,
  201. REPORTER& aReporter )
  202. {
  203. std::string libraryPath = GetFieldValue( &aFields, SIM_LIBRARY::LIBRARY_FIELD, aResolve, aDepth );
  204. std::string baseModelName = GetFieldValue( &aFields, SIM_LIBRARY::NAME_FIELD, aResolve, aDepth );
  205. if( libraryPath != "" )
  206. {
  207. return CreateModel( libraryPath, baseModelName, aFields, aResolve, aDepth, aPins, aReporter );
  208. }
  209. else
  210. {
  211. m_models.push_back( SIM_MODEL::Create( aFields, aResolve, aDepth, aPins, aReporter ) );
  212. return { baseModelName, *m_models.back() };
  213. }
  214. }
  215. SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const wxString& aLibraryPath,
  216. const std::string& aBaseModelName,
  217. const std::vector<SCH_FIELD>& aFields,
  218. bool aResolve, int aDepth,
  219. const std::vector<SCH_PIN*>& aPins,
  220. REPORTER& aReporter )
  221. {
  222. wxString msg;
  223. SIM_LIBRARY* library = nullptr;
  224. SIM_MODEL* baseModel = nullptr;
  225. std::string modelName;
  226. wxString path = ResolveLibraryPath( aLibraryPath, aReporter );
  227. auto it = m_libraries.find( path );
  228. if( it == m_libraries.end() )
  229. {
  230. it = m_libraries.emplace( path, SIM_LIBRARY::Create( path, m_forceFullParse, aReporter,
  231. [&]( const wxString& libPath, const wxString& relativeLib ) -> wxString
  232. {
  233. return ResolveEmbeddedLibraryPath( libPath, relativeLib, aReporter );
  234. } ) ).first;
  235. }
  236. library = &*it->second;
  237. if( aBaseModelName == "" )
  238. {
  239. msg.Printf( _( "Error loading simulation model: no '%s' field" ),
  240. SIM_LIBRARY::NAME_FIELD );
  241. aReporter.Report( msg, RPT_SEVERITY_ERROR );
  242. modelName = _( "unknown" ).ToStdString();
  243. }
  244. else if( library )
  245. {
  246. baseModel = library->FindModel( aBaseModelName );
  247. modelName = aBaseModelName;
  248. if( !baseModel )
  249. {
  250. msg.Printf( _( "Error loading simulation model: could not find base model '%s' "
  251. "in library '%s'" ),
  252. aBaseModelName,
  253. path );
  254. aReporter.Report( msg, RPT_SEVERITY_ERROR );
  255. }
  256. }
  257. m_models.push_back( SIM_MODEL::Create( baseModel, aPins, aFields, aResolve, aDepth, aReporter ) );
  258. return { modelName, *m_models.back() };
  259. }
  260. void SIM_LIB_MGR::SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel )
  261. {
  262. m_models.at( aIndex ) = std::move( aModel );
  263. }
  264. std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> SIM_LIB_MGR::GetLibraries() const
  265. {
  266. std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> libraries;
  267. for( auto& [path, library] : m_libraries )
  268. libraries.try_emplace( path, *library );
  269. return libraries;
  270. }
  271. std::vector<std::reference_wrapper<SIM_MODEL>> SIM_LIB_MGR::GetModels() const
  272. {
  273. std::vector<std::reference_wrapper<SIM_MODEL>> models;
  274. for( const std::unique_ptr<SIM_MODEL>& model : m_models )
  275. models.emplace_back( *model );
  276. return models;
  277. }