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