KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_http_lib.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) 2023 Andre F. K. Iwers <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
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 <wx/log.h>
23
24#include <fmt.h>
25#include <lib_symbol.h>
26#include <symbol_lib_table.h>
27
29#include "sch_io_http_lib.h"
30
31
33 SCH_IO( wxS( "HTTP library" ) ),
34 m_libTable( nullptr )
35{
36}
37
38
39void SCH_IO_HTTP_LIB::EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath,
40 const std::map<std::string, UTF8>* aProperties )
41{
42 std::vector<LIB_SYMBOL*> symbols;
43 EnumerateSymbolLib( symbols, aLibraryPath, aProperties );
44
45 for( LIB_SYMBOL* symbol : symbols )
46 aSymbolNameList.Add( symbol->GetName() );
47}
48
49
50void SCH_IO_HTTP_LIB::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList, const wxString& aLibraryPath,
51 const std::map<std::string, UTF8>* aProperties )
52{
53 wxCHECK_RET( m_libTable, _( "httplib plugin missing library table handle!" ) );
54 ensureSettings( aLibraryPath );
56
57 if( !m_conn )
59
60 bool powerSymbolsOnly = ( aProperties && aProperties->contains( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) );
61
62 for( const HTTP_LIB_CATEGORY& category : m_conn->getCategories() )
63 {
64 bool refresh_cache = true;
65
66 // Check if there is already a part in our cache, if not fetch it
67 if( m_cachedCategories.find( category.id ) != m_cachedCategories.end() )
68 {
69 // check if it's outdated, if so re-fetch
70 if( std::difftime( std::time( nullptr ), m_cachedCategories[category.id].lastCached )
71 < m_settings->m_Source.timeout_categories )
72 {
73 refresh_cache = false;
74 }
75 }
76
77 if( refresh_cache )
78 {
79 syncCache( category );
80 }
81
82 for( const HTTP_LIB_PART& part : m_cachedCategories[category.id].cachedParts )
83 {
84 wxString libIDString( part.name );
85
86 LIB_SYMBOL* symbol = loadSymbolFromPart( libIDString, category, part );
87
88 if( symbol && ( !powerSymbolsOnly || symbol->IsPower() ) )
89 aSymbolList.emplace_back( symbol );
90 }
91 }
92}
93
94
95LIB_SYMBOL* SCH_IO_HTTP_LIB::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
96 const std::map<std::string, UTF8>* aProperties )
97{
98 wxCHECK( m_libTable, nullptr );
99 ensureSettings( aLibraryPath );
101
102 if( !m_conn )
104
105 std::string part_id = "";
106
107 std::string partName( aAliasName.ToUTF8() );
108
109 const HTTP_LIB_CATEGORY* foundCategory = nullptr;
111
112 std::vector<HTTP_LIB_CATEGORY> categories = m_conn->getCategories();
113
114 if( m_conn->GetCachedParts().empty() )
115 syncCache();
116
117 std::tuple relations = m_conn->GetCachedParts()[partName];
118 std::string associatedCatID = std::get<1>( relations );
119
120 // get the matching category
121 for( const HTTP_LIB_CATEGORY& categoryIter : categories )
122 {
123 if( categoryIter.id == associatedCatID )
124 {
125 foundCategory = &categoryIter;
126 break;
127 }
128 }
129
130 // return Null if no category was found. This should never happen
131 if( foundCategory == nullptr )
132 {
133 wxLogTrace( traceHTTPLib, wxT( "loadSymbol: no category found for %s" ), partName );
134 return nullptr;
135 }
136
137 // get the matching query ID
138 for( const HTTP_LIB_PART& part : m_cachedCategories[foundCategory->id].cachedParts )
139 {
140 if( part.id == std::get<0>( relations ) )
141 {
142 part_id = part.id;
143 break;
144 }
145 }
146
147 if( m_conn->SelectOne( part_id, result ) )
148 {
149 wxLogTrace( traceHTTPLib, wxT( "LoadSymbol: SelectOne (%s) found in %s" ), part_id, foundCategory->name );
150 }
151 else
152 {
153 wxLogTrace( traceHTTPLib, wxT( "LoadSymbol: SelectOne (%s) failed for category %s" ), part_id,
154 foundCategory->name );
155
157 }
158
159 wxCHECK( foundCategory, nullptr );
160
161 return loadSymbolFromPart( aAliasName, *foundCategory, result );
162}
163
164
165void SCH_IO_HTTP_LIB::GetSubLibraryNames( std::vector<wxString>& aNames )
166{
167 ensureSettings( wxEmptyString );
168
169 aNames.clear();
170
171 std::set<wxString> categoryNames;
172
173 for( const HTTP_LIB_CATEGORY& categoryIter : m_conn->getCategories() )
174 {
175 if( categoryNames.count( categoryIter.name ) )
176 continue;
177
178 aNames.emplace_back( categoryIter.name );
179 categoryNames.insert( categoryIter.name );
180 }
181}
182
183
184wxString SCH_IO_HTTP_LIB::GetSubLibraryDescription( const wxString& aName )
185{
186 return m_conn->getCategoryDescription( std::string( aName.mb_str() ) );
187}
188
189
190void SCH_IO_HTTP_LIB::GetAvailableSymbolFields( std::vector<wxString>& aNames )
191{
192 // TODO: Implement this sometime; This is currently broken...
193 std::copy( m_customFields.begin(), m_customFields.end(), std::back_inserter( aNames ) );
194}
195
196
197void SCH_IO_HTTP_LIB::GetDefaultSymbolFields( std::vector<wxString>& aNames )
198{
199 std::copy( m_defaultShownFields.begin(), m_defaultShownFields.end(), std::back_inserter( aNames ) );
200}
201
202
203void SCH_IO_HTTP_LIB::ensureSettings( const wxString& aSettingsPath )
204{
205 auto tryLoad =
206 [&]()
207 {
208 if( !m_settings->LoadFromFile() )
209 {
210 THROW_IO_ERROR( wxString::Format( _( "HTTP library settings file %s missing or invalid." ),
211 aSettingsPath ) );
212 }
213
214 if( m_settings->m_Source.api_version.empty() )
215 {
216 THROW_IO_ERROR( wxString::Format( _( "HTTP library settings file %s is missing the API version "
217 "number." ),
218 aSettingsPath ) );
219 }
220
221 if( m_settings->getSupportedAPIVersion() != m_settings->m_Source.api_version )
222 {
223 THROW_IO_ERROR( wxString::Format( _( "HTTP library settings file %s uses API version %s, but "
224 "KiCad requires version %s." ),
225 aSettingsPath, m_settings->m_Source.api_version,
226 m_settings->getSupportedAPIVersion() ) );
227 }
228
229 if( m_settings->m_Source.root_url.empty() )
230 {
231 THROW_IO_ERROR( wxString::Format( _( "HTTP library settings file %s is missing the root URL." ),
232 aSettingsPath ) );
233 }
234
235 // map lib source type
236 m_settings->m_Source.type = m_settings->get_HTTP_LIB_SOURCE_TYPE();
237
238 if( m_settings->m_Source.type == HTTP_LIB_SOURCE_TYPE::INVALID )
239 {
240 THROW_IO_ERROR( wxString::Format( _( "HTTP library settings file %s has invalid library type." ),
241 aSettingsPath ) );
242 }
243
244 // make sure that the root url finishes with a forward slash
245 if( m_settings->m_Source.root_url.at( m_settings->m_Source.root_url.length() - 1 ) != '/' )
246 m_settings->m_Source.root_url += "/";
247
248 // Append api version to root URL
249 m_settings->m_Source.root_url += m_settings->m_Source.api_version + "/";
250 };
251
252 if( !m_settings && !aSettingsPath.IsEmpty() )
253 {
254 std::string path( aSettingsPath.ToUTF8() );
255 m_settings = std::make_unique<HTTP_LIB_SETTINGS>( path );
256
257 m_settings->SetReadOnly( true );
258
259 tryLoad();
260 }
261 else if( m_settings )
262 {
263 // If we have valid settings but no connection yet; reload settings in case user is editing
264 tryLoad();
265 }
266 else if( !m_settings )
267 {
268 wxLogTrace( traceHTTPLib, wxT( "ensureSettings: no settings available!" ) );
269 }
270}
271
272
274{
275 wxCHECK_RET( m_settings, "Call ensureSettings before ensureConnection!" );
276
277 connect();
278
279 if( !m_conn || !m_conn->IsValidEndpoint() )
280 {
281 THROW_IO_ERROR( wxString::Format( _( "Could not connect to %s. Errors: %s" ),
282 m_settings->m_Source.root_url,
283 m_lastError ) );
284 }
285}
286
287
289{
290 wxCHECK_RET( m_settings, "Call ensureSettings before connect()!" );
291
292 if( !m_conn )
293 {
294 m_conn = std::make_unique<HTTP_LIB_CONNECTION>( m_settings->m_Source, true );
295
296 if( !m_conn->IsValidEndpoint() )
297 {
298 m_lastError = m_conn->GetLastError();
299
300 // Make sure we release pointer so we are able to query API again next time
301 m_conn.reset();
302
303 return;
304 }
305 }
306}
307
308
310{
311 for( const HTTP_LIB_CATEGORY& category : m_conn->getCategories() )
312 syncCache( category );
313}
314
315
317{
318 std::vector<HTTP_LIB_PART> found_parts;
319
320 if( !m_conn->SelectAll( category, found_parts ) )
321 {
322 if( !m_conn->GetLastError().empty() )
323 {
324 THROW_IO_ERROR( wxString::Format( _( "Error retrieving data from HTTP library %s: %s" ),
325 category.name,
326 m_conn->GetLastError() ) );
327 }
328
329 return;
330 }
331
332 // remove cached parts
333 m_cachedCategories[category.id].cachedParts.clear();
334
335 // Copy newly cached data across
336 m_cachedCategories[category.id].cachedParts = found_parts;
337 m_cachedCategories[category.id].lastCached = std::time( nullptr );
338}
339
340
342 const HTTP_LIB_CATEGORY& aCategory,
343 const HTTP_LIB_PART& aPart )
344{
345 LIB_SYMBOL* symbol = nullptr;
346 LIB_SYMBOL* originalSymbol = nullptr;
347 LIB_ID symbolId;
348
349 std::string symbolIdStr = aPart.symbolIdStr;
350
351 // Get or Create the symbol using the found symbol
352 if( !symbolIdStr.empty() )
353 {
354 symbolId.Parse( symbolIdStr );
355
356 if( symbolId.IsValid() )
357 originalSymbol = m_libTable->LoadSymbol( symbolId );
358
359 if( originalSymbol )
360 {
361 wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: found original symbol '%s'" ), symbolIdStr );
362
363 symbol = originalSymbol->Duplicate();
364 symbol->SetSourceLibId( symbolId );
365
366 LIB_ID libId = symbol->GetLibId();
367 libId.SetSubLibraryName( aCategory.name );
368 symbol->SetLibId( libId );
369 }
370 else if( !symbolId.IsValid() )
371 {
372 wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: source symbol id '%s' is invalid, "
373 "will create empty symbol" ), symbolIdStr );
374 }
375 else
376 {
377 wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: source symbol '%s' not found, "
378 "will create empty symbol" ), symbolIdStr );
379 }
380 }
381
382 if( !symbol )
383 {
384 // Actual symbol not found: return metadata only; error will be
385 // indicated in the symbol chooser
386 symbol = new LIB_SYMBOL( aSymbolName );
387
388 LIB_ID libId = symbol->GetLibId();
389 libId.SetSubLibraryName( aCategory.name );
390 symbol->SetLibId( libId );
391 }
392
393 symbol->SetExcludedFromBOM( aPart.exclude_from_bom );
395 symbol->SetExcludedFromSim( aPart.exclude_from_sim );
396
397 wxArrayString fp_filters;
398
399 for( auto& [fieldName, fieldProperties] : aPart.fields )
400 {
401 wxString lowerFieldName = wxString( fieldName ).Lower();
402
403 if( lowerFieldName == footprint_field )
404 {
405 SCH_FIELD* field = &symbol->GetFootprintField();
406 wxStringTokenizer tokenizer( std::get<0>( fieldProperties ), ";\t\r\n", wxTOKEN_STRTOK );
407
408 while( tokenizer.HasMoreTokens() )
409 fp_filters.Add( tokenizer.GetNextToken() );
410
411 if( fp_filters.size() > 0 )
412 field->SetText( fp_filters[0] );
413
414 field->SetVisible( std::get<1>( fieldProperties ) );
415 }
416 else if( lowerFieldName == description_field )
417 {
418 SCH_FIELD* field = &symbol->GetDescriptionField();
419 field->SetText( std::get<0>( fieldProperties ) );
420 field->SetVisible( std::get<1>( fieldProperties ) );
421 }
422 else if( lowerFieldName == value_field )
423 {
424 SCH_FIELD* field = &symbol->GetValueField();
425 field->SetText( std::get<0>( fieldProperties ) );
426 field->SetVisible( std::get<1>( fieldProperties ) );
427 }
428 else if( lowerFieldName == datasheet_field )
429 {
430 SCH_FIELD* field = &symbol->GetDatasheetField();
431 field->SetText( std::get<0>( fieldProperties ) );
432 field->SetVisible( std::get<1>( fieldProperties ) );
433 }
434 else if( lowerFieldName == reference_field )
435 {
436 SCH_FIELD* field = &symbol->GetReferenceField();
437 field->SetText( std::get<0>( fieldProperties ) );
438 field->SetVisible( std::get<1>( fieldProperties ) );
439 }
440 else if( lowerFieldName == keywords_field )
441 {
442 symbol->SetKeyWords( std::get<0>( fieldProperties ) );
443 }
444 else
445 {
446 // Check if field exists, if so replace Text and adjust visiblity.
447 //
448 // This proves useful in situations where, for instance, an individual requires a particular value, such as
449 // the material type showcased at a specific position for a capacitor. Subsequently, this value could be defined
450 // in the symbol itself and then, potentially, be modified by the HTTP library as necessary.
451 SCH_FIELD* field = symbol->GetField( fieldName );
452
453 if( field != nullptr )
454 {
455 // adjust values accordingly
456 field->SetText( std::get<0>( fieldProperties ) );
457 field->SetVisible( std::get<1>( fieldProperties ) );
458 }
459 else
460 {
461 // Generic fields
462 field = new SCH_FIELD( symbol, FIELD_T::USER );
463 field->SetName( fieldName );
464
465 field->SetText( std::get<0>( fieldProperties ) );
466 field->SetVisible( std::get<1>( fieldProperties ) );
467 symbol->AddField( field );
468
469 m_customFields.insert( fieldName );
470 }
471 }
472 }
473
474 symbol->SetDescription( aPart.desc );
475 symbol->SetKeyWords( aPart.keywords );
476
477 for( const std::string& filter : aPart.fp_filters )
478 fp_filters.push_back( filter );
479
480 symbol->SetFPFilters( fp_filters );
481
482 return symbol;
483}
484
485void SCH_IO_HTTP_LIB::SaveSymbol( const wxString& aLibraryPath, const LIB_SYMBOL* aSymbol,
486 const std::map<std::string, UTF8>* aProperties )
487{
488 // TODO: Implement this sometime;
489}
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:387
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:52
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:172
void SetSubLibraryName(const UTF8 &aName)
Definition lib_id.h:131
Define a library symbol object.
Definition lib_symbol.h:85
SCH_FIELD & GetDescriptionField()
Return reference to the description field.
Definition lib_symbol.h:352
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:154
bool IsPower() const override
void SetSourceLibId(const LIB_ID &aLibId)
Definition lib_symbol.h:158
SCH_FIELD & GetDatasheetField()
Return reference to the datasheet field.
Definition lib_symbol.h:348
virtual LIB_SYMBOL * Duplicate() const
Create a copy of a LIB_SYMBOL and assigns unique KIIDs to the copy and its children.
Definition lib_symbol.h:100
SCH_FIELD * GetField(const wxString &aFieldName)
Find a field within this symbol matching aFieldName; return nullptr if not found.
SCH_FIELD & GetFootprintField()
Return reference to the footprint field.
Definition lib_symbol.h:344
void SetDescription(const wxString &aDescription)
Gets the Description field text value *‍/.
Definition lib_symbol.h:163
void SetKeyWords(const wxString &aKeyWords)
Definition lib_symbol.h:180
SCH_FIELD & GetValueField()
Return reference to the value field.
Definition lib_symbol.h:336
void SetFPFilters(const wxArrayString &aFilters)
Definition lib_symbol.h:215
void SetLibId(const LIB_ID &aLibId)
Definition lib_symbol.h:155
void AddField(SCH_FIELD *aField)
Add a field.
SCH_FIELD & GetReferenceField()
Return reference to the reference designator field.
Definition lib_symbol.h:340
void SetName(const wxString &aName)
void SetText(const wxString &aText) override
std::unique_ptr< HTTP_LIB_CONNECTION > m_conn
Generally will be null if no valid connection is established.
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
void SaveSymbol(const wxString &aLibraryPath, const LIB_SYMBOL *aSymbol, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aSymbol to an existing library located at aLibraryPath.
std::unique_ptr< HTTP_LIB_SETTINGS > m_settings
wxString GetSubLibraryDescription(const wxString &aName) override
Gets a description of a sublibrary.
void ensureSettings(const wxString &aSettingsPath)
SYMBOL_LIB_TABLE * m_libTable
std::set< wxString > m_customFields
void GetDefaultSymbolFields(std::vector< wxString > &aNames) override
Retrieves a list of (custom) field names that should be shown by default for this library in the symb...
void GetAvailableSymbolFields(std::vector< wxString > &aNames) override
Retrieves a list of (custom) field names that are present on symbols in this library.
std::set< wxString > m_defaultShownFields
wxString description_field
LIB_SYMBOL * loadSymbolFromPart(const wxString &aSymbolName, const HTTP_LIB_CATEGORY &aCategory, const HTTP_LIB_PART &aPart)
std::map< std::string, HTTP_LIB_CATEGORY > m_cachedCategories
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
void GetSubLibraryNames(std::vector< wxString > &aNames) override
Retrieves a list of sub-libraries in this library.
SCH_IO(const wxString &aName)
Definition sch_io.h:373
static const char * PropPowerSymsOnly
void SetExcludedFromBoard(bool aExcludeFromBoard) override
Set or clear exclude from board netlist flag.
Definition symbol.h:191
void SetExcludedFromSim(bool aExcludeFromSim) override
Set or clear the exclude from simulation flag.
Definition symbol.h:175
void SetExcludedFromBOM(bool aExcludeFromBOM) override
Set or clear the exclude from schematic bill of materials flag.
Definition symbol.h:185
#define _(s)
const char *const traceHTTPLib
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
std::string id
id of category
std::string name
name of category
std::string symbolIdStr
std::string keywords
std::vector< std::string > fp_filters
std::vector< std::pair< std::string, std::tuple< std::string, bool > > > fields
@ USER
The field ID hasn't been set yet; field is invalid.
wxString result
Test unit parsing edge cases and error handling.