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 { .load_status = LOAD_STATUS::LOAD_ERROR,
102 .error = LIBRARY_ERROR( { result.error() } ) };
103}
104
105
107{
108 SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::EnumFromStr( row->Type() );
109
110 if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN )
111 {
112 wxLogTrace( traceLibraries, "Sym: Plugin type %s is unknown!", row->Type() );
113 wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
114 return tl::unexpected( LIBRARY_ERROR( msg ) );
115 }
116
117 SCH_IO* plugin = SCH_IO_MGR::FindPlugin( type );
118 wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
119
120 plugin->SetLibraryManagerAdapter( this );
121
122 wxLogTrace( traceLibraries, "Sym: Library %s (%s) plugin created", row->Nickname(),
123 magic_enum::enum_name( row->Scope() ) );
124
125 return plugin;
126}
127
128
129std::vector<LIB_SYMBOL*> SYMBOL_LIBRARY_ADAPTER::GetSymbols( const wxString& aNickname, SYMBOL_TYPE aType )
130{
131 std::vector<LIB_SYMBOL*> symbols;
132
133 std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
134
135 if( !maybeLib )
136 return symbols;
137
138 const LIB_DATA* lib = *maybeLib;
139 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
140
141 if( aType == SYMBOL_TYPE::POWER_ONLY )
142 options[PropPowerSymsOnly] = "";
143
144 try
145 {
146 schplugin( lib )->EnumerateSymbolLib( symbols, getUri( lib->row ), &options );
147 }
148 catch( IO_ERROR& e )
149 {
150 wxLogTrace( traceLibraries, "Sym: Exception enumerating library %s: %s", lib->row->Nickname(), e.What() );
151 }
152
153 for( LIB_SYMBOL* symbol : symbols )
154 {
155 LIB_ID id = symbol->GetLibId();
156 id.SetLibNickname( lib->row->Nickname() );
157 symbol->SetLibId( id );
158 }
159
160 return symbols;
161}
162
163
164std::vector<wxString> SYMBOL_LIBRARY_ADAPTER::GetSymbolNames( const wxString& aNickname, SYMBOL_TYPE aType )
165{
166 // TODO(JE) can we kill wxArrayString in internal API?
167 wxArrayString namesAS;
168 std::vector<wxString> names;
169
170 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
171 {
172 const LIB_DATA* lib = *maybeLib;
173 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
174
175 if( aType == SYMBOL_TYPE::POWER_ONLY )
176 options[PropPowerSymsOnly] = "";
177
178 schplugin( lib )->EnumerateSymbolLib( namesAS, getUri( lib->row ), &options );
179 }
180
181 for( const wxString& name : namesAS )
182 names.emplace_back( name );
183
184 return names;
185}
186
187
188LIB_SYMBOL* SYMBOL_LIBRARY_ADAPTER::LoadSymbol( const wxString& aNickname, const wxString& aName )
189{
190 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
191 {
192 if( LIB_SYMBOL* symbol = schplugin( *lib )->LoadSymbol( getUri( ( *lib )->row ), aName ) )
193 {
194 LIB_ID id = symbol->GetLibId();
195 id.SetLibNickname( ( *lib )->row->Nickname() );
196 symbol->SetLibId( id );
197 return symbol;
198 }
199 }
200 else
201 {
202 wxLogTrace( traceLibraries, "LoadSymbol: requested library %s not loaded", aNickname );
203 }
204
205 return nullptr;
206}
207
208
210 const LIB_SYMBOL* aSymbol, bool aOverwrite )
211{
212 wxCHECK( aSymbol, SAVE_SKIPPED );
213
214 LIBRARY_RESULT<LIB_DATA*> libResult = loadIfNeeded( aNickname );
215
216 if( !libResult.has_value() )
217 {
218 wxLogTrace( traceLibraries, "SaveSymbol: unable to load library %s: %s", aNickname,
219 libResult.error().message );
220 return SAVE_SKIPPED;
221 }
222
223 LIB_DATA* lib = *libResult;
224
225 if( !lib )
226 {
227 wxLogTrace( traceLibraries, "SaveSymbol: library %s not found", aNickname );
228 return SAVE_SKIPPED;
229 }
230
231 SCH_IO* plugin = schplugin( lib );
232 wxCHECK( plugin, SAVE_SKIPPED );
233
234 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
235
236 if( !aOverwrite )
237 {
238 try
239 {
240 std::unique_ptr<LIB_SYMBOL> existing( plugin->LoadSymbol( getUri( lib->row ), aSymbol->GetName(),
241 &options ) );
242
243 if( existing )
244 return SAVE_SKIPPED;
245 }
246 catch( const IO_ERROR& e )
247 {
248 wxLogTrace( traceLibraries, "SaveSymbol: error checking for existing symbol %s:%s: %s", aNickname,
249 aSymbol->GetName(), e.What() );
250 return SAVE_SKIPPED;
251 }
252 }
253
254 try
255 {
256 plugin->SaveSymbol( getUri( lib->row ), aSymbol, &options );
257 }
258 catch( const IO_ERROR& e )
259 {
260 wxLogTrace( traceLibraries, "SaveSymbol: error saving %s:%s: %s", aNickname, aSymbol->GetName(), e.What() );
261 return SAVE_SKIPPED;
262 }
263
264 return SAVE_OK;
265}
266
267
268void SYMBOL_LIBRARY_ADAPTER::DeleteSymbol( const wxString& aNickname, const wxString& aSymbolName )
269{
270 wxCHECK_MSG( false, /* void */, "Unimplemented!" );
271}
272
273
275{
276 if( m_libraries.contains( aLib ) )
277 return m_libraries[aLib].plugin->IsLibraryWritable( getUri( m_libraries[aLib].row ) );
278
279 if( GlobalLibraries.contains( aLib ) )
280 return GlobalLibraries[aLib].plugin->IsLibraryWritable( getUri( GlobalLibraries[aLib].row ) );
281
282 return false;
283}
284
285
287{
288 // TODO(JE) any reason to clean these up earlier?
289 std::erase_if( m_futures,
290 []( const std::future<void>& aFuture )
291 {
292 return aFuture.valid();
293 } );
294
295 if( !m_futures.empty() )
296 {
297 wxLogTrace( traceLibraries, "Sym: Cannot AsyncLoad, futures from a previous call remain!" );
298 return;
299 }
300
301 std::vector<LIBRARY_TABLE_ROW*> rows = m_manager.Rows( LIBRARY_TABLE_TYPE::SYMBOL );
302
303 m_loadTotal = rows.size();
304 m_loadCount.store( 0 );
305
306 if( m_loadTotal == 0 )
307 {
308 wxLogTrace( traceLibraries, "Sym: AsyncLoad: no libraries left to load; exiting" );
309 return;
310 }
311
313
314 auto check =
315 []( const wxString& aLib, std::map<wxString, LIB_DATA>& aMap, std::mutex& aMutex )
316 {
317 std::lock_guard lock( aMutex );
318
319 if( aMap.contains( aLib ) )
320 {
321 if( aMap[aLib].status.load_status == LOAD_STATUS::LOADED )
322 return true;
323
324 aMap.erase( aLib );
325 }
326
327 return false;
328 };
329
330 for( const LIBRARY_TABLE_ROW* row : rows )
331 {
332 wxString nickname = row->Nickname();
333 LIBRARY_TABLE_SCOPE scope = row->Scope();
334
335 // TODO(JE) library tables -- check for modified global files
336 if( check( nickname, m_libraries, m_libraries_mutex ) )
337 {
338 --m_loadTotal;
339 continue;
340 }
341
342 if( check( nickname, GlobalLibraries, GlobalLibraryMutex ) )
343 {
344 --m_loadTotal;
345 continue;
346 }
347
348 m_futures.emplace_back( tp.submit_task(
349 [this, nickname, scope]()
350 {
351 if( m_abort.load() )
352 return;
353
354 LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( nickname );
355
356 if( result.has_value() )
357 {
358 LIB_DATA* lib = *result;
359 wxArrayString dummyList;
360 std::lock_guard lock ( lib->mutex );
361 lib->status.load_status = LOAD_STATUS::LOADING;
362
363 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
364
365 try
366 {
367 schplugin( lib )->EnumerateSymbolLib( dummyList, getUri( lib->row ), &options );
368 wxLogTrace( traceLibraries, "Sym: %s: library enumerated %zu items", nickname,
369 dummyList.size() );
370 lib->status.load_status = LOAD_STATUS::LOADED;
371 }
372 catch( IO_ERROR& e )
373 {
374 lib->status.load_status = LOAD_STATUS::LOAD_ERROR;
375 lib->status.error = LIBRARY_ERROR( { e.What() } );
376 wxLogTrace( traceLibraries, "Sym: %s: plugin threw exception: %s", nickname, e.What() );
377 }
378 }
379 else
380 {
381 switch( scope )
382 {
383 case LIBRARY_TABLE_SCOPE::GLOBAL:
384 {
385 std::lock_guard lock( GlobalLibraryMutex );
386
387 GlobalLibraries[nickname].status = LIB_STATUS( { .load_status = LOAD_STATUS::LOAD_ERROR,
388 .error = result.error() } );
389
390 break;
391 }
392
393 case LIBRARY_TABLE_SCOPE::PROJECT:
394 {
395 wxLogTrace( traceLibraries, "Sym: project library error: %s: %s", nickname,
396 result.error().message );
397 std::lock_guard lock( m_libraries_mutex );
398
399 m_libraries[nickname].status = LIB_STATUS( { .load_status = LOAD_STATUS::LOAD_ERROR,
400 .error = result.error() } );
401
402 break;
403 }
404
405 default:
406 wxFAIL_MSG( "Unexpected library table scope" );
407 }
408 }
409
410 ++m_loadCount;
411 }, BS::pr::lowest ) );
412 }
413
414 if( m_loadTotal )
415 wxLogTrace( traceLibraries, "Sym: Started async load of %zu libraries", m_loadTotal );
416}
417
418
419std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::GetLibraryStatus( const wxString& aNickname ) const
420{
421 if( m_libraries.contains( aNickname ) )
422 return m_libraries.at( aNickname ).status;
423
424 if( GlobalLibraries.contains( aNickname ) )
425 return GlobalLibraries.at( aNickname ).status;
426
427 return std::nullopt;
428}
429
430
431std::vector<wxString> SYMBOL_LIBRARY_ADAPTER::GetAvailableExtraFields( const wxString& aNickname )
432{
433 std::vector<wxString> fields;
434
435 if( std::optional<LIB_DATA*> result = fetchIfLoaded( aNickname ) )
436 {
437 LIB_DATA* rowData = *result;
438 int hash = schplugin( rowData )->GetModifyHash();
439
440 if( hash != rowData->modify_hash )
441 {
442 rowData->modify_hash = hash;
444 }
445
446 return rowData->available_fields_cache;
447 }
448
449 return fields;
450}
451
452
453bool SYMBOL_LIBRARY_ADAPTER::SupportsSubLibraries( const wxString& aNickname ) const
454{
455 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
456 {
457 const LIB_DATA* rowData = *result;
458 return schplugin( rowData )->SupportsSubLibraries();
459 }
460
461 return false;
462}
463
464
465std::vector<SUB_LIBRARY> SYMBOL_LIBRARY_ADAPTER::GetSubLibraries( const wxString& aNickname ) const
466{
467 std::vector<SUB_LIBRARY> ret;
468
469 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
470 {
471 const LIB_DATA* rowData = *result;
472 std::vector<wxString> names;
473 schplugin( rowData )->GetSubLibraryNames( names );
474
475 for( const wxString& name : names )
476 {
477 ret.emplace_back( SUB_LIBRARY { .nickname = name,
478 .description = schplugin( rowData )->GetSubLibraryDescription( name ) } );
479 }
480 }
481
482 return ret;
483}
484
485
486bool SYMBOL_LIBRARY_ADAPTER::SupportsConfigurationDialog( const wxString& aNickname ) const
487{
488 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
489 return ( *result )->plugin->SupportsConfigurationDialog();
490
491 return false;
492}
493
494
495void SYMBOL_LIBRARY_ADAPTER::ShowConfigurationDialog( const wxString& aNickname, wxWindow* aParent ) const
496{
497 std::optional<const LIB_DATA*> optRow = fetchIfLoaded( aNickname );
498
499 if( !optRow || !( *optRow )->plugin->SupportsConfigurationDialog() )
500 return;
501
502 DIALOG_SHIM* dialog = ( *optRow )->plugin->CreateConfigurationDialog( aParent );
503 dialog->ShowModal();
504}
505
506
508{
509 int hash = 0;
510
511 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
512 {
513 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( row->Nickname() ) )
514 {
515 const LIB_DATA* rowData = *result;
516 wxCHECK2( rowData->row, continue );
517 hash += schplugin( rowData )->GetModifyHash();
518 }
519 }
520
521 return hash;
522}
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.