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