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
23#include <common.h>
24#include <dialog_shim.h>
25#include <ki_exception.h>
26#include <wx/log.h>
27
28#include <lib_symbol.h>
30
31#include <env_vars.h>
32#include <pgm_base.h>
33#include <project.h>
34#include <thread_pool.h>
35#include <trace_helpers.h>
37
38
39const char* SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly = "pwr_sym_only";
40const char* SYMBOL_LIBRARY_ADAPTER::PropNonPowerSymsOnly = "non_pwr_sym_only";
41
42std::map<wxString, LIB_DATA> SYMBOL_LIBRARY_ADAPTER::GlobalLibraries;
43
45
46
51
52
57
58
60{
61 SCH_IO* ret = dynamic_cast<SCH_IO*>( aRow->plugin.get() );
62 wxCHECK( aRow->plugin && ret, nullptr );
63 return ret;
64}
65
66
67std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::LoadOne( const wxString& aNickname )
68{
69 if( LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( aNickname ); result.has_value() )
70 {
71 LIB_DATA* lib = *result;
72 std::lock_guard lock ( lib->mutex );
74
75 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
76
77 try
78 {
79 wxArrayString dummyList;
80 plugin( lib )->EnumerateSymbolLib( dummyList, getUri( lib->row ), &options );
81 wxLogTrace( traceLibraries, "Sym: %s: library enumerated %zu items", aNickname, dummyList.size() );
83 }
84 catch( IO_ERROR& e )
85 {
87 lib->status.error = LIBRARY_ERROR( { e.What() } );
88 wxLogTrace( traceLibraries, "Sym: %s: plugin threw exception: %s", aNickname, e.What() );
89 }
90
91 return lib->status;
92 }
93
94 return std::nullopt;
95}
96
97
99{
100 SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::EnumFromStr( row->Type() );
101
102 if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN )
103 {
104 wxLogTrace( traceLibraries, "Sym: Plugin type %s is unknown!", row->Type() );
105 wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
106 return tl::unexpected( LIBRARY_ERROR( msg ) );
107 }
108
109 SCH_IO* plugin = SCH_IO_MGR::FindPlugin( type );
110 wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
111
112 plugin->SetLibraryManagerAdapter( this );
113
114 wxLogTrace( traceLibraries, "Sym: Library %s (%s) plugin created",
115 row->Nickname(), magic_enum::enum_name( row->Scope() ) );
116
117 return plugin;
118}
119
120
121std::vector<LIB_SYMBOL*> SYMBOL_LIBRARY_ADAPTER::GetSymbols( const wxString& aNickname,
122 SYMBOL_TYPE aType )
123{
124 std::vector<LIB_SYMBOL*> symbols;
125
126 std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
127
128 if( !maybeLib )
129 return symbols;
130
131 const LIB_DATA* lib = *maybeLib;
132 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
133
134 if( aType == SYMBOL_TYPE::POWER_ONLY )
135 options[PropPowerSymsOnly] = "";
136
137 try
138 {
139 plugin( lib )->EnumerateSymbolLib( symbols, getUri( lib->row ), &options );
140 }
141 catch( IO_ERROR& e )
142 {
143 wxLogTrace( traceLibraries, "Sym: Exception enumerating library %s: %s",
144 lib->row->Nickname(), e.What() );
145 }
146
147 for( LIB_SYMBOL* symbol : symbols )
148 {
149 LIB_ID id = symbol->GetLibId();
150 id.SetLibNickname( lib->row->Nickname() );
151 symbol->SetLibId( id );
152 }
153
154 return symbols;
155}
156
157
158std::vector<wxString> SYMBOL_LIBRARY_ADAPTER::GetSymbolNames( const wxString& aNickname, SYMBOL_TYPE aType )
159{
160 // TODO(JE) can we kill wxArrayString in internal API?
161 wxArrayString namesAS;
162 std::vector<wxString> names;
163
164 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
165 {
166 const LIB_DATA* lib = *maybeLib;
167 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
168
169 if( aType == SYMBOL_TYPE::POWER_ONLY )
170 options[PropPowerSymsOnly] = "";
171
172 plugin( lib )->EnumerateSymbolLib( namesAS, getUri( lib->row ), &options );
173 }
174
175 for( const wxString& name : namesAS )
176 names.emplace_back( name );
177
178 return names;
179}
180
181
182LIB_SYMBOL* SYMBOL_LIBRARY_ADAPTER::LoadSymbol( const wxString& aNickname, const wxString& aName )
183{
184 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
185 {
186 if( LIB_SYMBOL* symbol = plugin( *lib )->LoadSymbol( getUri( ( *lib )->row ), aName ) )
187 {
188 LIB_ID id = symbol->GetLibId();
189 id.SetLibNickname( ( *lib )->row->Nickname() );
190 symbol->SetLibId( id );
191 return symbol;
192 }
193 }
194 else
195 {
196 wxLogTrace( traceLibraries, "LoadSymbol: requested library %s not loaded", aNickname );
197 }
198
199 return nullptr;
200}
201
202
204 const LIB_SYMBOL* aSymbol, bool aOverwrite )
205{
206 wxCHECK_MSG( false, SAVE_SKIPPED, "Unimplemented!" );
207}
208
209
210void SYMBOL_LIBRARY_ADAPTER::DeleteSymbol( const wxString& aNickname, const wxString& aSymbolName )
211{
212 wxCHECK_MSG( false, /* void */, "Unimplemented!" );
213}
214
215
217{
218 if( m_libraries.contains( aLib ) )
219 return m_libraries[aLib].plugin->IsLibraryWritable( getUri( m_libraries[aLib].row ) );
220
221 if( GlobalLibraries.contains( aLib ) )
222 return GlobalLibraries[aLib].plugin->IsLibraryWritable( getUri( GlobalLibraries[aLib].row ) );
223
224 return false;
225}
226
227
229{
230 // TODO(JE) any reason to clean these up earlier?
231 std::erase_if( m_futures,
232 []( const std::future<void>& aFuture ) { return aFuture.valid(); } );
233
234 if( !m_futures.empty() )
235 {
236 wxLogTrace( traceLibraries, "Sym: Cannot AsyncLoad, futures from a previous call remain!" );
237 return;
238 }
239
240 std::vector<LIBRARY_TABLE_ROW*> rows = m_manager.Rows( LIBRARY_TABLE_TYPE::SYMBOL );
241
242 m_loadTotal = rows.size();
243 m_loadCount.store( 0 );
244
245 if( m_loadTotal == 0 )
246 {
247 wxLogTrace( traceLibraries, "Sym: AsyncLoad: no libraries left to load; exiting" );
248 return;
249 }
250
252
253 auto check =
254 []( const wxString& aLib, std::map<wxString, LIB_DATA>& aMap, std::mutex& aMutex )
255 {
256 std::lock_guard lock( aMutex );
257
258 if( aMap.contains( aLib ) )
259 {
260 if( aMap[aLib].status.load_status == LOAD_STATUS::LOADED )
261 return true;
262
263 aMap.erase( aLib );
264 }
265
266 return false;
267 };
268
269 for( const LIBRARY_TABLE_ROW* row : rows )
270 {
271 wxString nickname = row->Nickname();
272 LIBRARY_TABLE_SCOPE scope = row->Scope();
273
274 // TODO(JE) library tables -- check for modified global files
275 if( check( nickname, m_libraries, m_libraries_mutex ) )
276 {
277 --m_loadTotal;
278 continue;
279 }
280
281 if( check( nickname, GlobalLibraries, GlobalLibraryMutex ) )
282 {
283 --m_loadTotal;
284 continue;
285 }
286
287 m_futures.emplace_back( tp.submit_task(
288 [this, nickname, scope]()
289 {
290 if( m_abort.load() )
291 return;
292
293 LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( nickname );
294
295 if( result.has_value() )
296 {
297 LIB_DATA* lib = *result;
298 wxArrayString dummyList;
299 std::lock_guard lock ( lib->mutex );
300 lib->status.load_status = LOAD_STATUS::LOADING;
301
302 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
303
304 try
305 {
306 plugin( lib )->EnumerateSymbolLib( dummyList, getUri( lib->row ), &options );
307 wxLogTrace( traceLibraries, "Sym: %s: library enumerated %zu items", nickname, dummyList.size() );
308 lib->status.load_status = LOAD_STATUS::LOADED;
309 }
310 catch( IO_ERROR& e )
311 {
312 lib->status.load_status = LOAD_STATUS::LOAD_ERROR;
313 lib->status.error = LIBRARY_ERROR( { e.What() } );
314 wxLogTrace( traceLibraries, "Sym: %s: plugin threw exception: %s", nickname, e.What() );
315 }
316 }
317 else
318 {
319 switch( scope )
320 {
321 case LIBRARY_TABLE_SCOPE::GLOBAL:
322 {
323 std::lock_guard lock( GlobalLibraryMutex );
324
325 GlobalLibraries[nickname].status = LIB_STATUS( {
326 .load_status = LOAD_STATUS::LOAD_ERROR,
327 .error = result.error()
328 } );
329
330 break;
331 }
332
333 case LIBRARY_TABLE_SCOPE::PROJECT:
334 {
335 wxLogTrace( traceLibraries, "Sym: project library error: %s: %s", nickname, result.error().message );
336 std::lock_guard lock( m_libraries_mutex );
337
338 m_libraries[nickname].status = LIB_STATUS( {
339 .load_status = LOAD_STATUS::LOAD_ERROR,
340 .error = result.error()
341 } );
342
343 break;
344 }
345
346 default:
347 wxFAIL_MSG( "Unexpected library table scope" );
348 }
349 }
350
351 ++m_loadCount;
352 } ) );
353 }
354
355 wxLogTrace( traceLibraries, "Sym: Started async load of %zu libraries", m_loadTotal );
356}
357
358
359std::optional<LIBRARY_ERROR> SYMBOL_LIBRARY_ADAPTER::LibraryError(
360 const wxString& aNickname ) const
361{
362 if( m_libraries.contains( aNickname ) )
363 {
364 return m_libraries.at( aNickname ).status.error;
365 }
366
367 if( GlobalLibraries.contains( aNickname ) )
368 {
369 return GlobalLibraries.at( aNickname ).status.error;
370 }
371
372 return std::nullopt;
373}
374
375
376bool SYMBOL_LIBRARY_ADAPTER::CreateLibrary( const wxString& aNickname )
377{
378 if( LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( aNickname ); result.has_value() )
379 {
380 LIB_DATA* data = *result;
381 std::map<std::string, UTF8> options = data->row->GetOptionsMap();
382
383 try
384 {
385 data->plugin->CreateLibrary( getUri( data->row ), &options );
386 return true;
387 }
388 catch( ... )
389 {
390 return false;
391 }
392 }
393
394 return false;
395}
396
397
398std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::GetLibraryStatus( const wxString& aNickname ) const
399{
400 if( m_libraries.contains( aNickname ) )
401 return m_libraries.at( aNickname ).status;
402
403 if( GlobalLibraries.contains( aNickname ) )
404 return GlobalLibraries.at( aNickname ).status;
405
406 return std::nullopt;
407}
408
409
410std::vector<std::pair<wxString, LIB_STATUS>> SYMBOL_LIBRARY_ADAPTER::GetLibraryStatuses() const
411{
412 std::vector<std::pair<wxString, LIB_STATUS>> ret;
413
414 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( LIBRARY_TABLE_TYPE::SYMBOL ) )
415 {
416 if( std::optional<LIB_STATUS> result = GetLibraryStatus( row->Nickname() ) )
417 {
418 ret.emplace_back( std::make_pair( row->Nickname(), *result ) );
419 }
420 else
421 {
422 // This should probably never happen, but until that can be proved...
423 ret.emplace_back( std::make_pair( row->Nickname(), LIB_STATUS( {
424 .load_status = LOAD_STATUS::LOAD_ERROR,
425 .error = LIBRARY_ERROR( _( "Library not found in library table" ) )
426 } ) ) );
427 }
428 }
429
430 return ret;
431}
432
433
435 const wxString& aNickname )
436{
437 std::vector<wxString> fields;
438
439 if( std::optional<LIB_DATA*> result = fetchIfLoaded( aNickname ) )
440 {
441 LIB_DATA* rowData = *result;
442 int hash = plugin( rowData )->GetModifyHash();
443
444 if( hash != rowData->modify_hash )
445 {
446 rowData->modify_hash = hash;
448 }
449
450 return rowData->available_fields_cache;
451 }
452
453 return fields;
454}
455
456
457bool SYMBOL_LIBRARY_ADAPTER::SupportsSubLibraries( const wxString& aNickname ) const
458{
459 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
460 {
461 const LIB_DATA* rowData = *result;
462 return plugin( rowData )->SupportsSubLibraries();
463 }
464
465 return false;
466}
467
468
470 const wxString& aNickname ) const
471{
472 std::vector<SUB_LIBRARY> ret;
473
474 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
475 {
476 const LIB_DATA* rowData = *result;
477 std::vector<wxString> names;
478 plugin( rowData )->GetSubLibraryNames( names );
479
480 for( const wxString& name : names )
481 {
482 ret.emplace_back( SUB_LIBRARY {
483 .nickname = name,
484 .description = plugin( rowData )->GetSubLibraryDescription( name )
485 } );
486 }
487 }
488
489 return ret;
490}
491
492
493bool SYMBOL_LIBRARY_ADAPTER::SupportsConfigurationDialog( const wxString& aNickname ) const
494{
495 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
496 return ( *result )->plugin->SupportsConfigurationDialog();
497
498 return false;
499}
500
501
503 wxWindow* aParent ) const
504{
505 std::optional<const LIB_DATA*> optRow = fetchIfLoaded( aNickname );
506
507 if( !optRow || !( *optRow )->plugin->SupportsConfigurationDialog() )
508 return;
509
510 DIALOG_SHIM* dialog = ( *optRow )->plugin->CreateConfigurationDialog( aParent );
511 dialog->ShowModal();
512}
513
514
516{
517 int hash = 0;
518
519 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
520 {
521 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( row->Nickname() ) )
522 {
523 const LIB_DATA* rowData = *result;
524 wxCHECK2( rowData->row, continue );
525 hash += plugin( rowData )->GetModifyHash();
526 }
527 }
528
529 return hash;
530}
531
532
533bool SYMBOL_LIBRARY_ADAPTER::IsWritable( const wxString& aNickname ) const
534{
535 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
536 {
537 const LIB_DATA* rowData = *result;
538 return rowData->plugin->IsLibraryWritable( getUri( rowData->row ) );
539 }
540
541 return false;
542}
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
virtual bool IsLibraryWritable(const wxString &aLibraryPath)
Return true if the library at aLibraryPath is writable.
Definition io_base.cpp:60
virtual void CreateLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr)
Create a new empty library at aLibraryPath empty.
Definition io_base.cpp:46
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:87
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
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.
bool CreateLibrary(const wxString &aNickname)
Creates the library (i.e. saves to disk) for the given row if it exists.
std::vector< wxString > GetSymbolNames(const wxString &aNickname, SYMBOL_TYPE aType=SYMBOL_TYPE::ALL_SYMBOLS)
bool SupportsSubLibraries(const wxString &aNickname) const
bool IsWritable(const wxString &aNickname) const override
Return true if the given nickname exists and is not a read-only library.
std::optional< LIB_STATUS > LoadOne(const wxString &aNickname)
Loads or reloads the given library, if it exists.
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
static SCH_IO * plugin(const LIB_DATA *aRow)
Helper to cast the ABC plugin in the LIB_DATA* to a concrete plugin.
SYMBOL_LIBRARY_ADAPTER(LIBRARY_MANAGER &aManager)
static wxString GlobalPathEnvVariableName()
static const char * PropPowerSymsOnly
std::vector< std::pair< wxString, LIB_STATUS > > GetLibraryStatuses() const
Returns a list of all library nicknames and their status (even if they failed to load)
LIBRARY_TABLE_TYPE Type() const override
The type of library table this adapter works with.
static std::map< wxString, LIB_DATA > GlobalLibraries
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< LIBRARY_ERROR > LibraryError(const wxString &aNickname) const
std::optional< LIB_STATUS > GetLibraryStatus(const wxString &aNickname) const
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::thread_pool< 0 > thread_pool
Definition thread_pool.h:31
wxLogTrace helper definitions.