KiCad PCB EDA Suite
Loading...
Searching...
No Matches
design_block_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
22#include "design_block.h"
23
24
26#include <design_block_io.h>
27#include <env_vars.h>
28#include <ki_exception.h>
29#include <thread_pool.h>
30#include <trace_helpers.h>
31
32#include <set>
33#include <magic_enum.hpp>
34#include <wx/log.h>
35
36
37std::map<wxString, LIB_DATA> DESIGN_BLOCK_LIBRARY_ADAPTER::GlobalLibraries;
38
40
41
46
47
49{
50 return ENV_VAR::GetVersionedEnvVarName( wxS( "DESIGN_BLOCK_DIR" ) );
51}
52
53
55{
56 DESIGN_BLOCK_IO* ret = dynamic_cast<DESIGN_BLOCK_IO*>( aRow->plugin.get() );
57 wxCHECK( aRow->plugin && ret, nullptr );
58 return ret;
59}
60
61
63{
64 // TODO(JE) do we maintain a precondition that URIs are absolute paths after expansion?
66
68 {
69 wxLogTrace( traceLibraries, "Sym: Plugin type %s is unknown!", row->Type() );
70 wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
71 return tl::unexpected( LIBRARY_ERROR( msg ) );
72 }
73
75 wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
76
77 return plugin;
78}
79
80
82{
83 return dbplugin( aRow );
84}
85
86
88std::optional<LIB_STATUS> DESIGN_BLOCK_LIBRARY_ADAPTER::LoadOne( LIB_DATA* aLib )
89{
90 wxArrayString dummyList;
91 std::lock_guard lock ( aLib->mutex );
93
94 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
95
96 try
97 {
98 dbplugin( aLib )->DesignBlockEnumerate( dummyList, getUri( aLib->row ), false, &options );
99 wxLogTrace( traceLibraries, "DB: %s: library enumerated %zu items", aLib->row->Nickname(), dummyList.size() );
101 }
102 catch( IO_ERROR& e )
103 {
105 aLib->status.error = LIBRARY_ERROR( { e.What() } );
106 wxLogTrace( traceLibraries, "DB: %s: plugin threw exception: %s", aLib->row->Nickname(), e.What() );
107 }
108
109 return aLib->status;
110}
111
112
113std::optional<LIB_STATUS> DESIGN_BLOCK_LIBRARY_ADAPTER::LoadOne( const wxString& nickname )
114{
116
117 if( result.has_value() )
118 return LoadOne( *result );
119
120 return LIB_STATUS{
121 .load_status = LOAD_STATUS::LOAD_ERROR,
122 .error = LIBRARY_ERROR( { result.error() } )
123 };
124}
125
126
128{
129 // TODO(JE) library tables - how much of this can be shared with other library types?
130 // TODO(JE) any reason to clean these up earlier?
131 std::erase_if( m_futures,
132 []( const std::future<void>& aFuture ) { return aFuture.valid(); } );
133
134 if( !m_futures.empty() )
135 {
136 wxLogTrace( traceLibraries, "DB: Cannot AsyncLoad, futures from a previous call remain!" );
137 return;
138 }
139
140 std::vector<LIBRARY_TABLE_ROW*> rows = m_manager.Rows( LIBRARY_TABLE_TYPE::DESIGN_BLOCK );
141
142 m_loadTotal = rows.size();
143 m_loadCount.store( 0 );
144
145 if( m_loadTotal == 0 )
146 {
147 wxLogTrace( traceLibraries, "DB: AsyncLoad: no libraries left to load; exiting" );
148 return;
149 }
150
152
153 auto check =
154 []( const wxString& aLib, std::map<wxString, LIB_DATA>& aMap, std::mutex& aMutex )
155 {
156 std::lock_guard lock( aMutex );
157
158 if( aMap.contains( aLib ) )
159 {
160 if( aMap[aLib].status.load_status == LOAD_STATUS::LOADED )
161 return true;
162
163 aMap.erase( aLib );
164 }
165
166 return false;
167 };
168
169 std::set<wxString> libNamesCurrentlyValid;
170
171 for( const LIBRARY_TABLE_ROW* row : rows )
172 {
173 LIBRARY_TABLE_SCOPE scope = row->Scope();
174 const wxString& nickname = row->Nickname();
175
176 libNamesCurrentlyValid.insert( nickname );
177
178 // TODO(JE) library tables -- check for modified global files
179 if( check( nickname, m_libraries, m_libraries_mutex ) )
180 {
181 --m_loadTotal;
182 continue;
183 }
184
185 if( check( nickname, GlobalLibraries, GlobalLibraryMutex ) )
186 {
187 --m_loadTotal;
188 continue;
189 }
190
191 m_futures.emplace_back( tp.submit_task(
192 [this, nickname, scope]()
193 {
194 if( m_abort.load() )
195 return;
196
197 LIBRARY_RESULT<LIB_DATA*> result = loadIfNeeded( nickname );
198
199 if( result.has_value() )
200 {
201 std::optional<LIB_STATUS> loadResult = LoadOne( *result );
202
203 // for design blocks, LoadOne should always return something
204 wxCHECK2( loadResult, ++m_loadCount; return );
205
206 switch( scope )
207 {
208 case LIBRARY_TABLE_SCOPE::GLOBAL:
209 {
210 std::lock_guard lock( GlobalLibraryMutex );
211 GlobalLibraries[nickname].status = *loadResult;
212 break;
213 }
214
215 case LIBRARY_TABLE_SCOPE::PROJECT:
216 {
217 std::lock_guard lock( m_libraries_mutex );
218 m_libraries[nickname].status = *loadResult;
219 break;
220 }
221
222 default:
223 wxFAIL_MSG( "Unexpected library table scope" );
224 }
225 }
226 else
227 {
228 switch( scope )
229 {
230 case LIBRARY_TABLE_SCOPE::GLOBAL:
231 {
232 std::lock_guard lock( GlobalLibraryMutex );
233
234 GlobalLibraries[nickname].status = LIB_STATUS( {
235 .load_status = LOAD_STATUS::LOAD_ERROR,
236 .error = result.error()
237 } );
238
239 break;
240 }
241
242 case LIBRARY_TABLE_SCOPE::PROJECT:
243 {
244 wxLogTrace( traceLibraries, "DB: project library error: %s: %s", nickname, result.error().message );
245 std::lock_guard lock( m_libraries_mutex );
246
247 m_libraries[nickname].status = LIB_STATUS( {
248 .load_status = LOAD_STATUS::LOAD_ERROR,
249 .error = result.error()
250 } );
251
252 break;
253 }
254
255 default:
256 wxFAIL_MSG( "Unexpected library table scope" );
257 }
258 }
259
260 ++m_loadCount;
261 }, BS::pr::lowest ) );
262 }
263
264 // Cleanup libraries that were removed from the table
265 {
266 std::lock_guard lock( GlobalLibraryMutex );
267 std::erase_if( GlobalLibraries, [&]( const auto& pair )
268 {
269 return !libNamesCurrentlyValid.contains( pair.first );
270 } );
271 }
272
273 {
274 std::lock_guard lock( m_libraries_mutex );
275 std::erase_if( m_libraries, [&]( const auto& pair )
276 {
277 return !libNamesCurrentlyValid.contains( pair.first );
278 } );
279 }
280
281 if( m_loadTotal > 0 )
282 wxLogTrace( traceLibraries, "DB: Started async load of %zu libraries", m_loadTotal );
283}
284
285
286std::vector<DESIGN_BLOCK*> DESIGN_BLOCK_LIBRARY_ADAPTER::GetDesignBlocks( const wxString& aNickname )
287{
288 std::vector<DESIGN_BLOCK*> blocks;
289
290 std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
291
292 if( !maybeLib )
293 return blocks;
294
295 const LIB_DATA* lib = *maybeLib;
296 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
297 wxArrayString blockNames;
298
299 try
300 {
301 dbplugin( lib )->DesignBlockEnumerate( blockNames, getUri( lib->row ), false, &options );
302 }
303 catch( IO_ERROR& e )
304 {
305 wxLogTrace( traceLibraries, "DB: Exception enumerating library %s: %s", lib->row->Nickname(), e.What() );
306 }
307
308 for( const wxString& blockName : blockNames )
309 {
310 try
311 {
312 blocks.emplace_back( dbplugin( lib )->DesignBlockLoad( getUri( lib->row ), blockName, false, &options ) );
313 }
314 catch( IO_ERROR& e )
315 {
316 wxLogTrace( traceLibraries, "DB: Exception enumerating design block %s: %s", blockName, e.What() );
317 }
318 }
319
320 return blocks;
321}
322
323
324std::vector<wxString> DESIGN_BLOCK_LIBRARY_ADAPTER::GetDesignBlockNames( const wxString& aNickname )
325{
326 // TODO(JE) can we kill wxArrayString in internal API?
327 wxArrayString namesAS;
328 std::vector<wxString> names;
329
330 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
331 {
332 const LIB_DATA* lib = *maybeLib;
333 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
334
335 dbplugin( lib )->DesignBlockEnumerate( namesAS, getUri( lib->row ), true, &options );
336 }
337
338 for( const wxString& name : namesAS )
339 names.emplace_back( name );
340
341 return names;
342}
343
344
346 const wxString& aDesignBlockName, bool aKeepUUID )
347{
348 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
349 {
350 const LIB_DATA* lib = *maybeLib;
351 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
352
353 DESIGN_BLOCK* db = dbplugin( lib )->DesignBlockLoad( getUri( lib->row ), aDesignBlockName, aKeepUUID, &options );
354 db->GetLibId().SetLibNickname( aNickname );
355 return db;
356 }
357
358 return nullptr;
359}
360
361
362bool DESIGN_BLOCK_LIBRARY_ADAPTER::DesignBlockExists( const wxString& aNickname, const wxString& aDesignBlockName )
363{
364 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
365 {
366 const LIB_DATA* lib = *maybeLib;
367 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
368
369 return dbplugin( lib )->DesignBlockExists( getUri( lib->row ), aDesignBlockName, &options );
370 }
371
372 return false;
373}
374
375
377 const wxString& aDesignBlockName )
378{
379 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
380 {
381 const LIB_DATA* lib = *maybeLib;
382 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
383
384 return dbplugin( lib )->GetEnumeratedDesignBlock( getUri( lib->row ), aDesignBlockName, &options );
385 }
386
387 return nullptr;
388}
389
390
392 const DESIGN_BLOCK* aDesignBlock,
393 bool aOverwrite )
394{
395 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
396 {
397 const LIB_DATA* lib = *maybeLib;
398 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
399
400 if( !aOverwrite && dbplugin( lib )->DesignBlockExists( getUri( lib->row ), aDesignBlock->GetName(), &options ) )
401 return SAVE_SKIPPED;
402
403 dbplugin( lib )->DesignBlockSave( getUri( lib->row ), aDesignBlock, &options );
404 }
405
406 return SAVE_OK;
407}
408
409
411 const wxString& aDesignBlockName )
412{
413 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
414 {
415 const LIB_DATA* lib = *maybeLib;
416 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
417 return dbplugin( lib )->DesignBlockDelete( getUri( lib->row ), aDesignBlockName, &options );
418 }
419}
420
421
423{
424 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
425 {
426 const LIB_DATA* lib = *maybeLib;
427 return plugin( lib )->IsLibraryWritable( getUri( lib->row ) );
428 }
429
430 return false;
431}
432
433
435 bool aKeepUUID )
436{
437 wxString nickname = aDesignBlockId.GetLibNickname();
438 wxString DesignBlockname = aDesignBlockId.GetLibItemName();
439
440 if( nickname.size() )
441 return LoadDesignBlock( nickname, DesignBlockname, aKeepUUID );
442
443 // nickname is empty, sequentially search (alphabetically) all libs/nicks for first match:
444 for( const wxString& library : GetLibraryNames() )
445 {
446 // DesignBlockLoad() returns NULL on not found, does not throw exception
447 // unless there's an IO_ERROR.
448 if( DESIGN_BLOCK* ret = LoadDesignBlock( library, DesignBlockname, aKeepUUID ) )
449 return ret;
450 }
451
452 return nullptr;
453}
454
const char * name
@ DESIGN_BLOCK_FILE_UNKNOWN
0 is not a legal menu id on Mac
static DESIGN_BLOCK_FILE_T EnumFromStr(const wxString &aFileType)
static DESIGN_BLOCK_IO * FindPlugin(DESIGN_BLOCK_FILE_T aFileType)
bool DesignBlockExists(const wxString &aLibraryPath, const wxString &aDesignBlockName, const std::map< std::string, UTF8 > *aProperties=nullptr)
void DesignBlockDelete(const wxString &aLibraryPath, const wxString &aDesignBlockName, const std::map< std::string, UTF8 > *aProperties=nullptr)
DESIGN_BLOCK * DesignBlockLoad(const wxString &aLibraryPath, const wxString &aDesignBlockName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr)
void DesignBlockSave(const wxString &aLibraryPath, const DESIGN_BLOCK *aDesignBlock, const std::map< std::string, UTF8 > *aProperties=nullptr)
const DESIGN_BLOCK * GetEnumeratedDesignBlock(const wxString &aLibraryPath, const wxString &aDesignBlockName, const std::map< std::string, UTF8 > *aProperties=nullptr)
void DesignBlockEnumerate(wxArrayString &aDesignBlockNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr)
std::vector< wxString > GetDesignBlockNames(const wxString &aNickname)
SAVE_T
The set of return values from DesignBlockSave() below.
std::vector< DESIGN_BLOCK * > GetDesignBlocks(const wxString &aNickname)
IO_BASE * plugin(const LIB_DATA *aRow) override
static DESIGN_BLOCK_IO * dbplugin(const LIB_DATA *aRow)
Helper to cast the ABC plugin in the LIB_DATA* to a concrete plugin.
void DeleteDesignBlock(const wxString &aNickname, const wxString &aDesignBlockName)
Delete the aDesignBlockName from the library given by aNickname.
LIBRARY_RESULT< IO_BASE * > createPlugin(const LIBRARY_TABLE_ROW *row) override
Creates a concrete plugin for the given row.
void AsyncLoad() override
Loads all available libraries for this adapter type in the background.
DESIGN_BLOCK * LoadDesignBlock(const wxString &aNickname, const wxString &aDesignBlockName, bool aKeepUUID=false)
Load a design block having aDesignBlockName from the library given by aNickname.
DESIGN_BLOCK * DesignBlockLoadWithOptionalNickname(const LIB_ID &aDesignBlockId, bool aKeepUUID=false)
Load a design block having aDesignBlockId with possibly an empty nickname.
SAVE_T SaveDesignBlock(const wxString &aNickname, const DESIGN_BLOCK *aDesignBlock, bool aOverwrite=true)
Write aDesignBlock to an existing library given by aNickname.
static std::map< wxString, LIB_DATA > GlobalLibraries
DESIGN_BLOCK_LIBRARY_ADAPTER(LIBRARY_MANAGER &aManager)
bool IsDesignBlockLibWritable(const wxString &aNickname)
Return true if the library given by aNickname is writable.
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
const DESIGN_BLOCK * GetEnumeratedDesignBlock(const wxString &aNickname, const wxString &aDesignBlockName)
A version of #DesignBlockLoad() for use after #DesignBlockEnumerate() for more efficient cache manage...
bool DesignBlockExists(const wxString &aNickname, const wxString &aDesignBlockName)
Indicates whether or not the given design block already exists in the given library.
const LIB_ID & GetLibId() const
wxString GetName() const override
virtual bool IsLibraryWritable(const wxString &aLibraryPath)
Return true if the library at aLibraryPath is writable.
Definition io_base.cpp:60
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< wxString > GetLibraryNames() const
Returns a list of library nicknames that are available (skips any that failed to load)
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
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
const UTF8 & GetLibItemName() const
Definition lib_id.h:102
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:87
#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
Storage for an actual loaded library (including library content owned by the plugin)
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
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.