KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_library_adapter.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <magic_enum.hpp>
22#include <memory>
23
24#include <common.h>
25#include <dialog_shim.h>
26#include <ki_exception.h>
27#include <wx/log.h>
28
29#include <lib_symbol.h>
31
32#include <env_vars.h>
33#include <pgm_base.h>
34#include <project.h>
35#include <thread_pool.h>
36#include <trace_helpers.h>
38
39
40const char* SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly = "pwr_sym_only";
41const char* SYMBOL_LIBRARY_ADAPTER::PropNonPowerSymsOnly = "non_pwr_sym_only";
42
43std::map<wxString, LIB_DATA> SYMBOL_LIBRARY_ADAPTER::GlobalLibraries;
44
46
47
52
53
58
59
61{
62 SCH_IO* ret = dynamic_cast<SCH_IO*>( aRow->plugin.get() );
63 wxCHECK( aRow->plugin && ret, nullptr );
64 return ret;
65}
66
67
69std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::LoadOne( LIB_DATA* aLib )
70{
71 std::lock_guard lock ( aLib->mutex );
73
74 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
75
76 try
77 {
78 wxArrayString dummyList;
79 schplugin( aLib )->EnumerateSymbolLib( dummyList, getUri( aLib->row ), &options );
80 wxLogTrace( traceLibraries, "Sym: %s: library enumerated %zu items", aLib->row->Nickname(), dummyList.size() );
82 }
83 catch( IO_ERROR& e )
84 {
86 aLib->status.error = LIBRARY_ERROR( { e.What() } );
87 wxLogTrace( traceLibraries, "Sym: %s: plugin threw exception: %s", aLib->row->Nickname(), e.What() );
88 }
89
90 return aLib->status;
91}
92
93
94std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::LoadOne( const wxString& nickname )
95{
97
98 if( result.has_value() )
99 return LoadOne( *result );
100
101 return LIB_STATUS{
102 .load_status = LOAD_STATUS::LOAD_ERROR,
103 .error = LIBRARY_ERROR( { result.error() } )
104 };
105}
106
107
109{
110 SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::EnumFromStr( row->Type() );
111
112 if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN )
113 {
114 wxLogTrace( traceLibraries, "Sym: Plugin type %s is unknown!", row->Type() );
115 wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
116 return tl::unexpected( LIBRARY_ERROR( msg ) );
117 }
118
119 SCH_IO* plugin = SCH_IO_MGR::FindPlugin( type );
120 wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
121
122 plugin->SetLibraryManagerAdapter( this );
123
124 wxLogTrace( traceLibraries, "Sym: Library %s (%s) plugin created",
125 row->Nickname(), magic_enum::enum_name( row->Scope() ) );
126
127 return plugin;
128}
129
130
131std::vector<LIB_SYMBOL*> SYMBOL_LIBRARY_ADAPTER::GetSymbols( const wxString& aNickname,
132 SYMBOL_TYPE aType )
133{
134 std::vector<LIB_SYMBOL*> symbols;
135
136 std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
137
138 if( !maybeLib )
139 return symbols;
140
141 const LIB_DATA* lib = *maybeLib;
142 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
143
144 if( aType == SYMBOL_TYPE::POWER_ONLY )
145 options[PropPowerSymsOnly] = "";
146
147 try
148 {
149 schplugin( lib )->EnumerateSymbolLib( symbols, getUri( lib->row ), &options );
150 }
151 catch( IO_ERROR& e )
152 {
153 wxLogTrace( traceLibraries, "Sym: Exception enumerating library %s: %s",
154 lib->row->Nickname(), e.What() );
155 }
156
157 for( LIB_SYMBOL* symbol : symbols )
158 {
159 LIB_ID id = symbol->GetLibId();
160 id.SetLibNickname( lib->row->Nickname() );
161 symbol->SetLibId( id );
162 }
163
164 return symbols;
165}
166
167
168std::vector<wxString> SYMBOL_LIBRARY_ADAPTER::GetSymbolNames( const wxString& aNickname, SYMBOL_TYPE aType )
169{
170 // TODO(JE) can we kill wxArrayString in internal API?
171 wxArrayString namesAS;
172 std::vector<wxString> names;
173
174 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
175 {
176 const LIB_DATA* lib = *maybeLib;
177 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
178
179 if( aType == SYMBOL_TYPE::POWER_ONLY )
180 options[PropPowerSymsOnly] = "";
181
182 schplugin( lib )->EnumerateSymbolLib( namesAS, getUri( lib->row ), &options );
183 }
184
185 for( const wxString& name : namesAS )
186 names.emplace_back( name );
187
188 return names;
189}
190
191
192LIB_SYMBOL* SYMBOL_LIBRARY_ADAPTER::LoadSymbol( const wxString& aNickname, const wxString& aName )
193{
194 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
195 {
196 if( LIB_SYMBOL* symbol = schplugin( *lib )->LoadSymbol( getUri( ( *lib )->row ), aName ) )
197 {
198 LIB_ID id = symbol->GetLibId();
199 id.SetLibNickname( ( *lib )->row->Nickname() );
200 symbol->SetLibId( id );
201 return symbol;
202 }
203 }
204 else
205 {
206 wxLogTrace( traceLibraries, "LoadSymbol: requested library %s not loaded", aNickname );
207 }
208
209 return nullptr;
210}
211
212
214 const LIB_SYMBOL* aSymbol, bool aOverwrite )
215{
216 wxCHECK( aSymbol, SAVE_SKIPPED );
217
218 LIBRARY_RESULT<LIB_DATA*> libResult = loadIfNeeded( aNickname );
219
220 if( !libResult.has_value() )
221 {
222 wxLogTrace( traceLibraries, "SaveSymbol: unable to load library %s: %s",
223 aNickname, libResult.error().message );
224 return SAVE_SKIPPED;
225 }
226
227 LIB_DATA* lib = *libResult;
228
229 if( !lib )
230 {
231 wxLogTrace( traceLibraries, "SaveSymbol: library %s not found", aNickname );
232 return SAVE_SKIPPED;
233 }
234
235 SCH_IO* plugin = schplugin( lib );
236 wxCHECK( plugin, SAVE_SKIPPED );
237
238 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
239
240 if( !aOverwrite )
241 {
242 try
243 {
244 std::unique_ptr<LIB_SYMBOL> existing( plugin->LoadSymbol( getUri( lib->row ),
245 aSymbol->GetName(),
246 &options ) );
247
248 if( existing )
249 return SAVE_SKIPPED;
250 }
251 catch( const IO_ERROR& e )
252 {
253 wxLogTrace( traceLibraries,
254 "SaveSymbol: error checking for existing symbol %s:%s: %s",
255 aNickname, aSymbol->GetName(), e.What() );
256 return SAVE_SKIPPED;
257 }
258 }
259
260 try
261 {
262 plugin->SaveSymbol( getUri( lib->row ), aSymbol, &options );
263 }
264 catch( const IO_ERROR& e )
265 {
266 wxLogTrace( traceLibraries, "SaveSymbol: error saving %s:%s: %s",
267 aNickname, aSymbol->GetName(), e.What() );
268 return SAVE_SKIPPED;
269 }
270
271 return SAVE_OK;
272}
273
274
275void SYMBOL_LIBRARY_ADAPTER::DeleteSymbol( const wxString& aNickname, const wxString& aSymbolName )
276{
277 wxCHECK_MSG( false, /* void */, "Unimplemented!" );
278}
279
280
282{
283 if( m_libraries.contains( aLib ) )
284 return m_libraries[aLib].plugin->IsLibraryWritable( getUri( m_libraries[aLib].row ) );
285
286 if( GlobalLibraries.contains( aLib ) )
287 return GlobalLibraries[aLib].plugin->IsLibraryWritable( getUri( GlobalLibraries[aLib].row ) );
288
289 return false;
290}
291
292
294{
295 // TODO(JE) any reason to clean these up earlier?
296 std::erase_if( m_futures,
297 []( const std::future<void>& aFuture ) { return aFuture.valid(); } );
298
299 if( !m_futures.empty() )
300 {
301 wxLogTrace( traceLibraries, "Sym: Cannot AsyncLoad, futures from a previous call remain!" );
302 return;
303 }
304
305 std::vector<LIBRARY_TABLE_ROW*> rows = m_manager.Rows( LIBRARY_TABLE_TYPE::SYMBOL );
306
307 m_loadTotal = rows.size();
308 m_loadCount.store( 0 );
309
310 if( m_loadTotal == 0 )
311 {
312 wxLogTrace( traceLibraries, "Sym: AsyncLoad: no libraries left to load; exiting" );
313 return;
314 }
315
317
318 auto check =
319 []( const wxString& aLib, std::map<wxString, LIB_DATA>& aMap, std::mutex& aMutex )
320 {
321 std::lock_guard lock( aMutex );
322
323 if( aMap.contains( aLib ) )
324 {
325 if( aMap[aLib].status.load_status == LOAD_STATUS::LOADED )
326 return true;
327
328 aMap.erase( aLib );
329 }
330
331 return false;
332 };
333
334 for( const LIBRARY_TABLE_ROW* row : rows )
335 {
336 wxString nickname = row->Nickname();
337 LIBRARY_TABLE_SCOPE scope = row->Scope();
338
339 // TODO(JE) library tables -- check for modified global files
340 if( check( nickname, m_libraries, m_libraries_mutex ) )
341 {
342 --m_loadTotal;
343 continue;
344 }
345
346 if( check( nickname, GlobalLibraries, GlobalLibraryMutex ) )
347 {
348 --m_loadTotal;
349 continue;
350 }
351
352 m_futures.emplace_back( tp.submit_task(
353 [this, nickname, scope]()
354 {
355 if( m_abort.load() )
356 return;
357
358 LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( nickname );
359
360 if( result.has_value() )
361 {
362 LIB_DATA* lib = *result;
363 wxArrayString dummyList;
364 std::lock_guard lock ( lib->mutex );
365 lib->status.load_status = LOAD_STATUS::LOADING;
366
367 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
368
369 try
370 {
371 schplugin( lib )->EnumerateSymbolLib( dummyList, getUri( lib->row ), &options );
372 wxLogTrace( traceLibraries, "Sym: %s: library enumerated %zu items", nickname, dummyList.size() );
373 lib->status.load_status = LOAD_STATUS::LOADED;
374 }
375 catch( IO_ERROR& e )
376 {
377 lib->status.load_status = LOAD_STATUS::LOAD_ERROR;
378 lib->status.error = LIBRARY_ERROR( { e.What() } );
379 wxLogTrace( traceLibraries, "Sym: %s: plugin threw exception: %s", nickname, e.What() );
380 }
381 }
382 else
383 {
384 switch( scope )
385 {
386 case LIBRARY_TABLE_SCOPE::GLOBAL:
387 {
388 std::lock_guard lock( GlobalLibraryMutex );
389
390 GlobalLibraries[nickname].status = LIB_STATUS( {
391 .load_status = LOAD_STATUS::LOAD_ERROR,
392 .error = result.error()
393 } );
394
395 break;
396 }
397
398 case LIBRARY_TABLE_SCOPE::PROJECT:
399 {
400 wxLogTrace( traceLibraries, "Sym: project library error: %s: %s", nickname, result.error().message );
401 std::lock_guard lock( m_libraries_mutex );
402
403 m_libraries[nickname].status = LIB_STATUS( {
404 .load_status = LOAD_STATUS::LOAD_ERROR,
405 .error = result.error()
406 } );
407
408 break;
409 }
410
411 default:
412 wxFAIL_MSG( "Unexpected library table scope" );
413 }
414 }
415
416 ++m_loadCount;
417 }, BS::pr::lowest ) );
418 }
419
420 if( m_loadTotal )
421 wxLogTrace( traceLibraries, "Sym: Started async load of %zu libraries", m_loadTotal );
422}
423
424
425std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::GetLibraryStatus( const wxString& aNickname ) const
426{
427 if( m_libraries.contains( aNickname ) )
428 return m_libraries.at( aNickname ).status;
429
430 if( GlobalLibraries.contains( aNickname ) )
431 return GlobalLibraries.at( aNickname ).status;
432
433 return std::nullopt;
434}
435
436
438 const wxString& aNickname )
439{
440 std::vector<wxString> fields;
441
442 if( std::optional<LIB_DATA*> result = fetchIfLoaded( aNickname ) )
443 {
444 LIB_DATA* rowData = *result;
445 int hash = schplugin( rowData )->GetModifyHash();
446
447 if( hash != rowData->modify_hash )
448 {
449 rowData->modify_hash = hash;
451 }
452
453 return rowData->available_fields_cache;
454 }
455
456 return fields;
457}
458
459
460bool SYMBOL_LIBRARY_ADAPTER::SupportsSubLibraries( const wxString& aNickname ) const
461{
462 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
463 {
464 const LIB_DATA* rowData = *result;
465 return schplugin( rowData )->SupportsSubLibraries();
466 }
467
468 return false;
469}
470
471
473 const wxString& aNickname ) const
474{
475 std::vector<SUB_LIBRARY> ret;
476
477 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
478 {
479 const LIB_DATA* rowData = *result;
480 std::vector<wxString> names;
481 schplugin( rowData )->GetSubLibraryNames( names );
482
483 for( const wxString& name : names )
484 {
485 ret.emplace_back( SUB_LIBRARY {
486 .nickname = name,
487 .description = schplugin( rowData )->GetSubLibraryDescription( name )
488 } );
489 }
490 }
491
492 return ret;
493}
494
495
496bool SYMBOL_LIBRARY_ADAPTER::SupportsConfigurationDialog( const wxString& aNickname ) const
497{
498 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
499 return ( *result )->plugin->SupportsConfigurationDialog();
500
501 return false;
502}
503
504
506 wxWindow* aParent ) const
507{
508 std::optional<const LIB_DATA*> optRow = fetchIfLoaded( aNickname );
509
510 if( !optRow || !( *optRow )->plugin->SupportsConfigurationDialog() )
511 return;
512
513 DIALOG_SHIM* dialog = ( *optRow )->plugin->CreateConfigurationDialog( aParent );
514 dialog->ShowModal();
515}
516
517
519{
520 int hash = 0;
521
522 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
523 {
524 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( row->Nickname() ) )
525 {
526 const LIB_DATA* rowData = *result;
527 wxCHECK2( rowData->row, continue );
528 hash += schplugin( rowData )->GetModifyHash();
529 }
530 }
531
532 return hash;
533}
const char * name
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:68
int ShowModal() override
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
LIBRARY_MANAGER_ADAPTER(LIBRARY_MANAGER &aManager)
Constructs a type-specific adapter into the library manager.
LIBRARY_RESULT< LIB_DATA * > loadIfNeeded(const wxString &aNickname)
Fetches a loaded library, triggering a load of that library if it isn't loaded yet.
std::map< wxString, LIB_DATA > m_libraries
std::vector< std::future< void > > m_futures
static wxString getUri(const LIBRARY_TABLE_ROW *aRow)
std::atomic< size_t > m_loadCount
LIBRARY_MANAGER & m_manager
std::optional< const LIB_DATA * > fetchIfLoaded(const wxString &aNickname) const
LIBRARY_TABLE_SCOPE Scope() const
std::map< std::string, UTF8 > GetOptionsMap() const
const wxString & Type() const
const wxString & Nickname() const
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:100
Define a library symbol object.
Definition lib_symbol.h:83
wxString GetName() const override
Definition lib_symbol.h:146
static SCH_FILE_T EnumFromStr(const wxString &aFileType)
Return the #SCH_FILE_T from the corresponding plugin type name: "kicad", "legacy",...
Base class that schematic file and library loading and saving plugins should derive from.
Definition sch_io.h:59
virtual void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr)
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
Definition sch_io.cpp:82
virtual bool SupportsSubLibraries() const
Definition sch_io.h:290
virtual int GetModifyHash() const =0
Return the modification hash from the library cache.
virtual void GetAvailableSymbolFields(std::vector< wxString > &aNames)
Retrieves a list of (custom) field names that are present on symbols in this library.
Definition sch_io.h:326
virtual LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aPartName, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
Definition sch_io.cpp:100
virtual wxString GetSubLibraryDescription(const wxString &aName)
Gets a description of a sublibrary.
Definition sch_io.h:315
virtual void GetSubLibraryNames(std::vector< wxString > &aNames)
Retrieves a list of sub-libraries in this library.
Definition sch_io.h:304
bool SupportsConfigurationDialog(const wxString &aNickname) const override
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
static SCH_IO * schplugin(const LIB_DATA *aRow)
Helper to cast the ABC plugin in the LIB_DATA* to a concrete plugin.
LIB_SYMBOL * LoadSymbol(const wxString &aNickname, const wxString &aName)
Load a LIB_SYMBOL having aName from the library given by aNickname.
void DeleteSymbol(const wxString &aNickname, const wxString &aSymbolName)
Deletes the aSymbolName from the library given by aNickname.
std::vector< wxString > GetSymbolNames(const wxString &aNickname, SYMBOL_TYPE aType=SYMBOL_TYPE::ALL_SYMBOLS)
bool SupportsSubLibraries(const wxString &aNickname) const
SAVE_T SaveSymbol(const wxString &aNickname, const LIB_SYMBOL *aSymbol, bool aOverwrite=true)
Write aSymbol to an existing library given by aNickname.
LIBRARY_RESULT< IO_BASE * > createPlugin(const LIBRARY_TABLE_ROW *row) override
Creates a concrete plugin for the given row.
std::vector< SUB_LIBRARY > GetSubLibraries(const wxString &aNickname) const
SYMBOL_LIBRARY_ADAPTER(LIBRARY_MANAGER &aManager)
static wxString GlobalPathEnvVariableName()
static const char * PropPowerSymsOnly
LIBRARY_TABLE_TYPE Type() const override
The type of library table this adapter works with.
static std::map< wxString, LIB_DATA > GlobalLibraries
IO_BASE * plugin(const LIB_DATA *aRow) override
std::vector< LIB_SYMBOL * > GetSymbols(const wxString &aNickname, SYMBOL_TYPE aType=SYMBOL_TYPE::ALL_SYMBOLS)
void AsyncLoad() override
Loads all available libraries for this adapter type in the background.
std::vector< wxString > GetAvailableExtraFields(const wxString &aNickname)
Returns a list of additional (non-mandatory) symbol fields present in the given library.
void ShowConfigurationDialog(const wxString &aNickname, wxWindow *aParent) const override
SAVE_T
The set of return values from SaveSymbol() below.
static const char * PropNonPowerSymsOnly
std::optional< LIB_STATUS > GetLibraryStatus(const wxString &aNickname) const override
Returns the status of a loaded library, or nullopt if the library hasn't been loaded (yet)
bool IsSymbolLibWritable(const wxString &aNickname)
Return true if the library given by aNickname is writable.
static std::mutex GlobalLibraryMutex
The common library.
#define _(s)
Functions related to environment variables, including help functions.
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
tl::expected< ResultType, LIBRARY_ERROR > LIBRARY_RESULT
LIBRARY_TABLE_SCOPE
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:77
see class PGM_BASE
Storage for an actual loaded library (including library content owned by the plugin)
std::vector< wxString > available_fields_cache
LIB_STATUS status
std::unique_ptr< IO_BASE > plugin
std::mutex mutex
const LIBRARY_TABLE_ROW * row
The overall status of a loaded or loading library.
std::optional< LIBRARY_ERROR > error
LOAD_STATUS load_status
A descriptor for a sub-library (supported by database and http libraries)
wxString result
Test unit parsing edge cases and error handling.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:31
wxLogTrace helper definitions.