KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_lib_mgr.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 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
25#include <filename_resolver.h>
26#include <pgm_base.h>
27#include <string>
28#include <string_utils.h>
29#include <common.h>
30#include <functional>
31#include <sch_symbol.h>
32#include <schematic.h>
33
34// Include simulator headers after wxWidgets headers to avoid conflicts with Windows headers
35// (especially on msys2 + wxWidgets 3.0.x)
36#include <sim/sim_lib_mgr.h>
37#include <sim/sim_library.h>
38#include <sim/sim_model.h>
39#include <sim/sim_model_ideal.h>
40
41using namespace std::placeholders;
42
43
45 m_project( aPrj ),
46 m_forceFullParse( false )
47{
48}
49
50
52{
53 m_libraries.clear();
54 m_models.clear();
55}
56
57
58wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, REPORTER& aReporter )
59{
61
62 resolver.SetProject( m_project );
63
64 std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
65
66 for( const EMBEDDED_FILES* embeddedFiles : m_embeddedFilesStack )
67 embeddedFilesStack.push_back( embeddedFiles );
68
69 wxString resolvedPath = resolver.ResolvePath( aLibraryPath, wxEmptyString,
70 std::move( embeddedFilesStack ) );
71
72 // If FILENAME_RESOLVER found the file, use that result
73 if( !resolvedPath.IsEmpty() )
74 {
75 wxFileName fn( resolvedPath );
76
77 if( fn.IsAbsolute() )
78 return fn.GetFullPath();
79 }
80
81 // Fall back to the original behavior for paths not found by FILENAME_RESOLVER.
82 // First expand any environment variables in the path.
83 wxString expandedPath = ExpandEnvVarSubstitutions( aLibraryPath, m_project );
84
85 // Convert to UNIX format
86 expandedPath.Replace( '\\', '/' );
87
88 wxFileName fn( expandedPath );
89
90 if( fn.IsAbsolute() && fn.Exists() )
91 return fn.GetFullPath();
92
93 wxFileName projectFn( m_project ? m_project->AbsolutePath( expandedPath ) : expandedPath );
94
95 if( projectFn.Exists() )
96 return projectFn.GetFullPath();
97
98 // Check relative to SPICE_LIB_DIR environment variable
99 wxFileName spiceLibFn( expandedPath );
100 wxString spiceLibDir;
101
102 wxGetEnv( wxT( "SPICE_LIB_DIR" ), &spiceLibDir );
103
104 if( !spiceLibDir.IsEmpty() && spiceLibFn.MakeAbsolute( spiceLibDir ) && spiceLibFn.Exists() )
105 return spiceLibFn.GetFullPath();
106
107 if( spiceLibDir.IsEmpty() || spiceLibFn.GetFullPath() == projectFn.GetFullPath() )
108 {
109 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s'" ),
110 projectFn.GetFullPath() ) );
111 }
112 else
113 {
114 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s' or '%s'" ),
115 projectFn.GetFullPath(),
116 spiceLibFn.GetFullPath() ) );
117 }
118
119 return aLibraryPath;
120}
121
122
123wxString SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const wxString& aLibPath,
124 const wxString& aRelativeLib,
125 REPORTER& aReporter )
126{
127 wxFileName testPath( aLibPath );
128 wxString fullPath( aLibPath );
129
130 if( !testPath.IsAbsolute() && !aRelativeLib.empty() )
131 {
132 wxString relLib( aRelativeLib );
133
134 relLib = ResolveLibraryPath( relLib, aReporter );
135
136 wxFileName fn( relLib );
137
138 testPath.MakeAbsolute( fn.GetPath( true ) );
139 fullPath = testPath.GetFullPath();
140 }
141
142 wxFileName fn( fullPath );
143
144 if( !fn.Exists() )
145 fullPath = aLibPath;
146
147 fullPath = ResolveLibraryPath( fullPath, aReporter );
148
149 return fullPath;
150}
151
152
153void SIM_LIB_MGR::SetLibrary( const wxString& aLibraryPath, REPORTER& aReporter )
154{
155 wxString path = ResolveLibraryPath( aLibraryPath, aReporter );
156
158 return;
159
160 if( !wxFileName::Exists( path ) )
161 {
162 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s'" ),
163 path ) );
164 return;
165 }
166
167 std::unique_ptr<SIM_LIBRARY> library = SIM_LIBRARY::Create( path, m_forceFullParse, aReporter,
168 [&]( const wxString& libPath, const wxString& relativeLib ) -> wxString
169 {
170 return ResolveEmbeddedLibraryPath( libPath, relativeLib, aReporter );
171 } );
172
173 Clear();
174 m_libraries[path] = std::move( library );
175}
176
177
178SIM_MODEL& SIM_LIB_MGR::CreateModel( SIM_MODEL::TYPE aType, const std::vector<SCH_PIN*>& aPins,
179 REPORTER& aReporter )
180{
181 m_models.push_back( SIM_MODEL::Create( aType, aPins, aReporter ) );
182 return *m_models.back();
183}
184
185
187 const std::vector<SCH_PIN*>& aPins, REPORTER& aReporter )
188{
189 m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aReporter ) );
190 return *m_models.back();
191}
192
193
194SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel, const std::vector<SCH_PIN*>& aPins,
195 const std::vector<SCH_FIELD>& aFields, bool aResolve, int aDepth,
196 REPORTER& aReporter )
197{
198 m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aFields, aResolve, aDepth, aReporter ) );
199 return *m_models.back();
200}
201
203 bool aResolve, int aDepth, const wxString& aVariantName,
204 REPORTER& aReporter, const wxString& aMergedSimPins )
205{
206 // Note: currently this creates a resolved model (all Kicad variables references are resolved
207 // before building the model).
208 //
209 // That's not what we want if this is ever called from the Simulation Model Editor (or other
210 // editors, but it is what we want if called to generate a netlist or other exported items.
211
212
213 std::vector<SCH_FIELD> fields;
214
215 for( const SCH_FIELD& field : aSymbol.GetFields() )
216 {
217 if( field.GetId() == FIELD_T::REFERENCE )
218 {
219 fields.emplace_back( &aSymbol, FIELD_T::USER, field.GetName() );
220 fields.back().SetText( aSymbol.GetRef( aSheetPath ) );
221 }
222 else if( field.GetId() == FIELD_T::VALUE || field.GetName().StartsWith( wxS( "Sim." ) ) )
223 {
224 fields.emplace_back( &aSymbol, FIELD_T::USER, field.GetName() );
225
226 // For multi-unit symbols, use merged Sim.Pins from all units if provided
227 if( !aMergedSimPins.IsEmpty() && field.GetName() == SIM_PINS_FIELD )
228 fields.back().SetText( aMergedSimPins );
229 else
230 fields.back().SetText( field.GetShownText( aSheetPath, false, aDepth, aVariantName ) );
231 }
232 }
233
234 auto getOrCreateField =
235 [&aSymbol, &fields]( const wxString& name ) -> SCH_FIELD*
236 {
237 for( SCH_FIELD& field : fields )
238 {
239 if( field.GetName().IsSameAs( name ) )
240 return &field;
241 }
242
243 fields.emplace_back( &aSymbol, FIELD_T::USER, name );
244 return &fields.back();
245 };
246
247 wxString deviceType;
248 wxString modelType;
249 wxString modelParams;
250 wxString pinMap;
251 bool storeInValue = false;
252
253 // Infer RLC and VI models if they aren't specified
254 if( SIM_MODEL::InferSimModel( aSymbol, &fields, aResolve, aDepth, SIM_VALUE_GRAMMAR::NOTATION::SI,
255 &deviceType, &modelType, &modelParams, &pinMap ) )
256 {
257 getOrCreateField( SIM_DEVICE_FIELD )->SetText( deviceType );
258
259 if( !modelType.IsEmpty() )
260 getOrCreateField( SIM_DEVICE_SUBTYPE_FIELD )->SetText( modelType );
261
262 getOrCreateField( SIM_PARAMS_FIELD )->SetText( modelParams );
263 getOrCreateField( SIM_PINS_FIELD )->SetText( pinMap );
264
265 storeInValue = true;
266 }
267
268 std::vector<SCH_PIN*> sourcePins = aSymbol.GetAllLibPins();
269
270 std::sort( sourcePins.begin(), sourcePins.end(),
271 []( const SCH_PIN* lhs, const SCH_PIN* rhs )
272 {
273 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
274 } );
275
276 SIM_LIBRARY::MODEL model = CreateModel( fields, aResolve, aDepth, sourcePins, aReporter );
277
278 model.model.SetIsStoredInValue( storeInValue );
279
280 return model;
281}
282
283
284SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<SCH_FIELD>& aFields,
285 bool aResolve, int aDepth,
286 const std::vector<SCH_PIN*>& aPins,
287 REPORTER& aReporter )
288{
289 std::string libraryPath = GetFieldValue( &aFields, SIM_LIBRARY::LIBRARY_FIELD, aResolve, aDepth );
290 std::string baseModelName = GetFieldValue( &aFields, SIM_LIBRARY::NAME_FIELD, aResolve, aDepth );
291
292 if( libraryPath != "" )
293 {
294 return CreateModel( libraryPath, baseModelName, aFields, aResolve, aDepth, aPins, aReporter );
295 }
296 else
297 {
298 m_models.push_back( SIM_MODEL::Create( aFields, aResolve, aDepth, aPins, aReporter ) );
299 return { baseModelName, *m_models.back() };
300 }
301}
302
303
304SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const wxString& aLibraryPath,
305 const std::string& aBaseModelName,
306 const std::vector<SCH_FIELD>& aFields,
307 bool aResolve, int aDepth,
308 const std::vector<SCH_PIN*>& aPins,
309 REPORTER& aReporter )
310{
311 wxString msg;
312 SIM_LIBRARY* library = nullptr;
313 SIM_MODEL* baseModel = nullptr;
314 std::string modelName;
315 wxString path = ResolveLibraryPath( aLibraryPath, aReporter );
316
317 auto it = m_libraries.find( path );
318
319 if( it == m_libraries.end() )
320 {
321 it = m_libraries.emplace( path, SIM_LIBRARY::Create( path, m_forceFullParse, aReporter,
322 [&]( const wxString& libPath, const wxString& relativeLib ) -> wxString
323 {
324 return ResolveEmbeddedLibraryPath( libPath, relativeLib, aReporter );
325 } ) ).first;
326 }
327
328 library = &*it->second;
329
330 if( aBaseModelName == "" )
331 {
332 msg.Printf( _( "Error loading simulation model: no '%s' field" ),
334
335 aReporter.Report( msg, RPT_SEVERITY_ERROR );
336
337 modelName = _( "unknown" ).ToStdString();
338 }
339 else if( library )
340 {
341 baseModel = library->FindModel( aBaseModelName );
342 modelName = aBaseModelName;
343
344 if( !baseModel )
345 {
346 msg.Printf( _( "Error loading simulation model: could not find base model '%s' "
347 "in library '%s'" ),
348 aBaseModelName,
349 path );
350
351 aReporter.Report( msg, RPT_SEVERITY_ERROR );
352 }
353 }
354
355 m_models.push_back( SIM_MODEL::Create( baseModel, aPins, aFields, aResolve, aDepth, aReporter ) );
356
357 return { modelName, *m_models.back() };
358}
359
360
361void SIM_LIB_MGR::SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel )
362{
363 m_models.at( aIndex ) = std::move( aModel );
364}
365
366
367std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> SIM_LIB_MGR::GetLibraries() const
368{
369 std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> libraries;
370
371 for( auto& [path, library] : m_libraries )
372 libraries.try_emplace( path, *library );
373
374 return libraries;
375}
376
377
378std::vector<std::reference_wrapper<SIM_MODEL>> SIM_LIB_MGR::GetModels() const
379{
380 std::vector<std::reference_wrapper<SIM_MODEL>> models;
381
382 for( const std::unique_ptr<SIM_MODEL>& model : m_models )
383 models.emplace_back( *model );
384
385 return models;
386}
const char * name
Provide an extensible class to resolve 3D model paths.
Container for project specific data.
Definition project.h:65
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
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition reporter.h:143
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Schematic symbol object.
Definition sch_symbol.h:76
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
std::vector< SCH_PIN * > GetAllLibPins() const
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
static std::unique_ptr< SIM_LIBRARY > Create(const wxString &aFilePath, bool aForceFullParse, REPORTER &aReporter, const std::function< wxString(const wxString &, const wxString &)> &aResolver)
Read library from a source file (e.g.
static constexpr auto LIBRARY_FIELD
Definition sim_library.h:35
static constexpr auto NAME_FIELD
Definition sim_library.h:36
void SetModel(int aIndex, std::unique_ptr< SIM_MODEL > aModel)
wxString ResolveLibraryPath(const wxString &aLibraryPath, REPORTER &aReporter)
const PROJECT * m_project
Definition sim_lib_mgr.h:90
std::vector< EMBEDDED_FILES * > m_embeddedFilesStack
Definition sim_lib_mgr.h:89
bool m_forceFullParse
Definition sim_lib_mgr.h:91
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
SIM_LIB_MGR(const PROJECT *aPrj)
std::vector< std::unique_ptr< SIM_MODEL > > m_models
Definition sim_lib_mgr.h:93
std::map< wxString, std::reference_wrapper< const SIM_LIBRARY > > GetLibraries() const
wxString ResolveEmbeddedLibraryPath(const wxString &aLibPath, const wxString &aRelativeLib, REPORTER &aReporter)
std::vector< std::reference_wrapper< SIM_MODEL > > GetModels() const
void SetLibrary(const wxString &aLibraryPath, REPORTER &aReporter)
std::map< wxString, std::unique_ptr< SIM_LIBRARY > > m_libraries
Definition sim_lib_mgr.h:92
static bool InferSimModel(T &aSymbol, std::vector< SCH_FIELD > *aFields, bool aResolve, int aDepth, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
static std::unique_ptr< SIM_MODEL > Create(TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition common.cpp:558
The common library.
#define _(s)
static FILENAME_RESOLVER * resolver
see class PGM_BASE
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
wxString GetFieldValue(const std::vector< SCH_FIELD > *aFields, FIELD_T aFieldType)
Definition sch_field.h:403
#define SIM_PINS_FIELD
Definition sim_model.h:54
#define SIM_DEVICE_FIELD
Definition sim_model.h:52
#define SIM_PARAMS_FIELD
Definition sim_model.h:55
#define SIM_DEVICE_SUBTYPE_FIELD
Definition sim_model.h:53
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
KIBIS_MODEL * model