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.

370 lines
12 KiB

  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 (C) 2022-2023 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 <pgm_base.h>
  25. #include <string>
  26. #include <string_utils.h>
  27. #include <common.h>
  28. #include <functional>
  29. #include <sch_symbol.h>
  30. // Include simulator headers after wxWidgets headers to avoid conflicts with Windows headers
  31. // (especially on msys2 + wxWidgets 3.0.x)
  32. #include <sim/sim_lib_mgr.h>
  33. #include <sim/sim_library.h>
  34. #include <sim/sim_model.h>
  35. #include <sim/sim_model_ideal.h>
  36. using namespace std::placeholders;
  37. SIM_LIB_MGR::SIM_LIB_MGR( const PROJECT* aPrj, REPORTER* aReporter ) :
  38. m_project( aPrj ),
  39. m_reporter( aReporter )
  40. {
  41. }
  42. void SIM_LIB_MGR::Clear()
  43. {
  44. m_libraries.clear();
  45. m_models.clear();
  46. }
  47. wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, const PROJECT* aProject )
  48. {
  49. wxString expandedPath = ExpandEnvVarSubstitutions( aLibraryPath, aProject );
  50. wxFileName fn( expandedPath );
  51. if( fn.IsAbsolute() )
  52. return fn.GetFullPath();
  53. wxFileName projectFn( aProject ? aProject->AbsolutePath( expandedPath ) : expandedPath );
  54. if( projectFn.Exists() )
  55. return projectFn.GetFullPath();
  56. wxFileName spiceLibFn( expandedPath );
  57. wxString spiceLibDir;
  58. wxGetEnv( wxT( "SPICE_LIB_DIR" ), &spiceLibDir );
  59. if( !spiceLibDir.IsEmpty() && spiceLibFn.MakeAbsolute( spiceLibDir ) && spiceLibFn.Exists() )
  60. return spiceLibFn.GetFullPath();
  61. if( projectFn.GetFullPath() == spiceLibFn.GetFullPath() )
  62. {
  63. THROW_IO_ERROR( wxString::Format( _( "Simulation model library not found at '%s'" ),
  64. spiceLibFn.GetFullPath() ) );
  65. }
  66. else
  67. {
  68. THROW_IO_ERROR( wxString::Format( _( "Simulation model library not found at '%s' or '%s'" ),
  69. projectFn.GetFullPath(),
  70. spiceLibFn.GetFullPath() ) );
  71. }
  72. }
  73. std::string SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const std::string& aLibPath,
  74. const std::string& aRelativeLib )
  75. {
  76. wxFileName testPath( aLibPath );
  77. wxString fullPath( aLibPath );
  78. if( !testPath.IsAbsolute() && !aRelativeLib.empty() )
  79. {
  80. wxString relLib( aRelativeLib );
  81. try
  82. {
  83. relLib = ResolveLibraryPath( relLib, m_project );
  84. }
  85. catch( ... )
  86. {}
  87. wxFileName fn( relLib );
  88. testPath.MakeAbsolute( fn.GetPath( true ) );
  89. fullPath = testPath.GetFullPath();
  90. }
  91. try
  92. {
  93. wxFileName fn( fullPath );
  94. if( !fn.Exists() )
  95. fullPath = aLibPath;
  96. fullPath = ResolveLibraryPath( fullPath, m_project );
  97. }
  98. catch( ... )
  99. {}
  100. return fullPath.ToStdString();
  101. }
  102. void SIM_LIB_MGR::SetLibrary( const wxString& aLibraryPath )
  103. {
  104. try
  105. {
  106. wxString path = ResolveLibraryPath( aLibraryPath, m_project );
  107. std::function<std::string(const std::string&, const std::string&)> f2 =
  108. std::bind( &SIM_LIB_MGR::ResolveEmbeddedLibraryPath, this, _1, _2 );
  109. std::unique_ptr<SIM_LIBRARY> library = SIM_LIBRARY::Create( path, m_reporter, &f2 );
  110. Clear();
  111. m_libraries[path] = std::move( library );
  112. }
  113. catch( const IO_ERROR& e )
  114. {
  115. m_reporter->Report( e.What() );
  116. }
  117. }
  118. SIM_MODEL& SIM_LIB_MGR::CreateModel( SIM_MODEL::TYPE aType, const std::vector<LIB_PIN*>& aPins )
  119. {
  120. m_models.push_back( SIM_MODEL::Create( aType, aPins, m_reporter ) );
  121. return *m_models.back();
  122. }
  123. SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel,
  124. const std::vector<LIB_PIN*>& aPins )
  125. {
  126. m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, m_reporter ) );
  127. return *m_models.back();
  128. }
  129. template <typename T>
  130. SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel,
  131. const std::vector<LIB_PIN*>& aPins,
  132. const std::vector<T>& aFields )
  133. {
  134. m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aFields, m_reporter ) );
  135. return *m_models.back();
  136. }
  137. template SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel,
  138. const std::vector<LIB_PIN*>& aPins,
  139. const std::vector<SCH_FIELD>& aFields );
  140. template SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel,
  141. const std::vector<LIB_PIN*>& aPins,
  142. const std::vector<LIB_FIELD>& aFields );
  143. SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const SCH_SHEET_PATH* aSheetPath, SCH_SYMBOL& aSymbol )
  144. {
  145. // Note: currently this creates a resolved model (all Kicad variables references are resolved
  146. // before building the model).
  147. //
  148. // That's not what we want if this is ever called from the Simulation Model Editor (or other
  149. // editors, but it is what we want if called to generate a netlist or other exported items.
  150. std::vector<SCH_FIELD> fields;
  151. for( int i = 0; i < aSymbol.GetFieldCount(); ++i )
  152. {
  153. fields.emplace_back( VECTOR2I(), i, &aSymbol, aSymbol.GetFields()[ i ].GetName() );
  154. if( i == REFERENCE_FIELD )
  155. fields.back().SetText( aSymbol.GetRef( aSheetPath ) );
  156. else
  157. fields.back().SetText( aSymbol.GetFields()[ i ].GetShownText( 0, false ) );
  158. }
  159. wxString deviceType;
  160. wxString modelType;
  161. wxString modelParams;
  162. wxString pinMap;
  163. bool storeInValue = false;
  164. // Infer RLC and VI models if they aren't specified
  165. if( SIM_MODEL::InferSimModel( aSymbol, &fields, true, SIM_VALUE_GRAMMAR::NOTATION::SI,
  166. &deviceType, &modelType, &modelParams, &pinMap ) )
  167. {
  168. fields.emplace_back( &aSymbol, -1, SIM_DEVICE_TYPE_FIELD );
  169. fields.back().SetText( deviceType );
  170. if( !modelType.IsEmpty() )
  171. {
  172. fields.emplace_back( &aSymbol, -1, SIM_TYPE_FIELD );
  173. fields.back().SetText( modelType );
  174. }
  175. fields.emplace_back( &aSymbol, -1, SIM_PARAMS_FIELD );
  176. fields.back().SetText( modelParams );
  177. fields.emplace_back( &aSymbol, -1, SIM_PINS_FIELD );
  178. fields.back().SetText( pinMap );
  179. storeInValue = true;
  180. }
  181. std::vector<LIB_PIN*> sourcePins = aSymbol.GetAllLibPins();
  182. std::sort( sourcePins.begin(), sourcePins.end(),
  183. []( const LIB_PIN* lhs, const LIB_PIN* rhs )
  184. {
  185. return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
  186. } );
  187. SIM_LIBRARY::MODEL model = CreateModel( fields, sourcePins, true );
  188. model.model.SetIsStoredInValue( storeInValue );
  189. return model;
  190. }
  191. template <typename T>
  192. SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<T>& aFields,
  193. const std::vector<LIB_PIN*>& aPins, bool aResolved )
  194. {
  195. std::string libraryPath = SIM_MODEL::GetFieldValue( &aFields, SIM_LIBRARY::LIBRARY_FIELD );
  196. std::string baseModelName = SIM_MODEL::GetFieldValue( &aFields, SIM_LIBRARY::NAME_FIELD );
  197. if( libraryPath != "" )
  198. {
  199. return CreateModel( libraryPath, baseModelName, aFields, aPins );
  200. }
  201. else
  202. {
  203. m_models.push_back( SIM_MODEL::Create( aFields, aPins, aResolved, m_reporter ) );
  204. return { baseModelName, *m_models.back() };
  205. }
  206. }
  207. template SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<SCH_FIELD>& aFields,
  208. const std::vector<LIB_PIN*>& aPins,
  209. bool aResolved );
  210. template SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<LIB_FIELD>& aFields,
  211. const std::vector<LIB_PIN*>& aPins,
  212. bool aResolved );
  213. template <typename T>
  214. SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const wxString& aLibraryPath,
  215. const std::string& aBaseModelName,
  216. const std::vector<T>& aFields,
  217. const std::vector<LIB_PIN*>& aPins )
  218. {
  219. wxString path;
  220. wxString msg;
  221. SIM_LIBRARY* library = nullptr;
  222. SIM_MODEL* baseModel = nullptr;
  223. std::string modelName;
  224. try
  225. {
  226. path = ResolveLibraryPath( aLibraryPath, m_project );
  227. std::function<std::string( const std::string&, const std::string& )> f2 =
  228. std::bind( &SIM_LIB_MGR::ResolveEmbeddedLibraryPath, this, _1, _2 );
  229. auto it = m_libraries.try_emplace( path, SIM_LIBRARY::Create( path, m_reporter, &f2 ) ).first;
  230. library = &*it->second;
  231. }
  232. catch( const IO_ERROR& e )
  233. {
  234. if( m_reporter )
  235. {
  236. msg.Printf( _( "Error loading simulation model library '%s': %s" ),
  237. path,
  238. e.What() );
  239. m_reporter->Report( msg, RPT_SEVERITY_ERROR );
  240. }
  241. }
  242. if( aBaseModelName == "" )
  243. {
  244. if( m_reporter )
  245. {
  246. msg.Printf( _( "Error loading simulation model: no '%s' field" ),
  247. SIM_LIBRARY::NAME_FIELD );
  248. m_reporter->Report( msg, RPT_SEVERITY_ERROR );
  249. }
  250. modelName = _( "unknown" ).ToStdString();
  251. }
  252. else if( library )
  253. {
  254. baseModel = library->FindModel( aBaseModelName );
  255. modelName = aBaseModelName;
  256. if( !baseModel )
  257. {
  258. if( m_reporter )
  259. {
  260. msg.Printf( _( "Error loading simulation model: could not find base model '%s' "
  261. "in library '%s'" ),
  262. aBaseModelName,
  263. path );
  264. m_reporter->Report( msg, RPT_SEVERITY_ERROR );
  265. }
  266. }
  267. }
  268. m_models.push_back( SIM_MODEL::Create( baseModel, aPins, aFields, m_reporter ) );
  269. return { modelName, *m_models.back() };
  270. }
  271. void SIM_LIB_MGR::SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel )
  272. {
  273. m_models.at( aIndex ) = std::move( aModel );
  274. }
  275. std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> SIM_LIB_MGR::GetLibraries() const
  276. {
  277. std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> libraries;
  278. for( auto& [path, library] : m_libraries )
  279. libraries.try_emplace( path, *library );
  280. return libraries;
  281. }
  282. std::vector<std::reference_wrapper<SIM_MODEL>> SIM_LIB_MGR::GetModels() const
  283. {
  284. std::vector<std::reference_wrapper<SIM_MODEL>> models;
  285. for( const std::unique_ptr<SIM_MODEL>& model : m_models )
  286. models.emplace_back( *model );
  287. return models;
  288. }