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
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21
22#include <algorithm>
23#include <chrono>
24#include <string_view>
25
26#include <bs_thread_pool.hpp>
27#include <wx/log.h>
28#include <wx/tokenzr.h>
29
30#include <fmt.h>
31#include <hash.h>
32#include <lib_symbol.h>
33
36#include "sch_io_http_lib.h"
37#include <ki_exception.h>
38
39
40static bool fetchCategoryParts( HTTP_LIB_CONNECTION& aConn, const HTTP_LIB_CATEGORY& aCategory,
41 std::vector<HTTP_LIB_PART>& aParts )
42{
43 if( !aConn.SelectAll( aCategory, aParts ) )
44 return false;
45
46 for( HTTP_LIB_PART& part : aParts )
47 {
48 if( part.detailsLoaded )
49 continue;
50
51 if( HTTP_LIB_PART fullPart; aConn.SelectOne( part.id, fullPart ) )
52 {
53 // The detail record may omit description/keywords info that the category record had
54 if( fullPart.desc.empty() )
55 fullPart.desc = part.desc;
56
57 if( fullPart.keywords.empty() )
58 fullPart.keywords = part.keywords;
59
60 fullPart.id = part.id;
61 fullPart.name = part.name;
62 part = std::move( fullPart );
63 }
64 }
65
66 return true;
67}
68
69
71 SCH_IO( wxS( "HTTP library" ) ),
72 m_adapter( nullptr )
73{
74}
75
76
78{
79 // The wait predicate and shutdown need the same mutex to avoid a lost wakeup.
80 {
81 std::lock_guard lock( m_refreshMutex );
82 m_refreshRunning = false;
83 }
84
85 m_refreshCV.notify_all();
86
87 if( m_refreshThread.joinable() )
88 m_refreshThread.join();
89}
90
91
93{
94 if( m_refreshRunning.exchange( true ) )
95 return;
96
97 wxLogTrace( traceHTTPLib, wxT( "Starting background refresh thread" ) );
99}
100
101
103{
104 BS::this_thread::set_os_thread_name( "httplib bg" );
105
106 while( m_refreshRunning.load() )
107 {
108 long long maxAge = 0;
109
110 {
111 std::shared_lock lock( m_cacheMutex );
112
113 if( m_settings )
114 maxAge = std::max( m_settings->m_Source.timeout_categories, m_settings->m_Source.timeout_parts );
115 }
116
117 if( maxAge <= 0 )
118 maxAge = 1;
119
120 // Hold a shared lock on m_cacheMutex while accessing m_conn so connect() (which takes a
121 // unique lock to reset/replace m_conn) can't destroy the object out from under us.
122 std::shared_lock connGuard( m_cacheMutex );
123
124 if( m_conn && m_cachePopulated.load() )
125 {
126 wxLogTrace( traceHTTPLib, wxT( "Initiating background refresh" ) );
127
128 try
129 {
130 std::map<std::string, HTTP_LIB_CATEGORY> categoryData;
131 bool fetchSuccess = true;
132
133 for( const HTTP_LIB_CATEGORY& category : m_conn->getCategories() )
134 {
135 std::vector<HTTP_LIB_PART> foundParts;
136
137 if( !fetchCategoryParts( *m_conn, category, foundParts ) )
138 {
139 wxLogTrace( traceHTTPLib, wxT( "Background refresh: fetch failed for category %s" ),
140 category.name );
141 fetchSuccess = false;
142 break;
143 }
144
145 HTTP_LIB_CATEGORY cached = category;
146 cached.cachedParts = std::move( foundParts );
147 categoryData[category.id] = std::move( cached );
148 }
149
150 if( fetchSuccess )
151 {
152 size_t signature = computeSignature( categoryData );
153 bool dataChanged = false;
154
155 {
156 connGuard.unlock();
157 std::unique_lock lock( m_cacheMutex );
158
159 if( signature != m_cacheSignature )
160 dataChanged = true;
161 }
162
163 if( dataChanged )
164 {
165 wxLogTrace( traceHTTPLib, wxT( "Background refresh: new data" ) );
166
167 materializeCache( m_libraryPath, categoryData );
168
169 {
170 std::unique_lock lock( m_cacheMutex );
171 m_cacheSignature = signature;
172 }
173 }
174 else
175 {
176 wxLogTrace( traceHTTPLib, wxT( "Background refresh: no new data" ) );
177 }
178 }
179 }
180 catch( const IO_ERROR& e )
181 {
182 wxLogTrace( traceHTTPLib, wxT( "Background refresh failed: %s" ), e.What() );
183 }
184 catch( const std::exception& e )
185 {
186 wxLogTrace( traceHTTPLib, wxT( "Background refresh failed: %s" ), e.what() );
187 }
188 }
189
190 {
191 std::unique_lock lock( m_refreshMutex );
192
193 m_refreshCV.wait_for( lock, std::chrono::seconds( maxAge ),
194 [this]()
195 {
196 return !m_refreshRunning.load();
197 } );
198 }
199 }
200}
201
202
203size_t SCH_IO_HTTP_LIB::computeSignature( const std::map<std::string, HTTP_LIB_CATEGORY>& aCategoryData ) const
204{
205 size_t signature = 0;
206
207 for( const auto& [catId, category] : aCategoryData )
208 {
209 hash_combine( signature, std::string_view( catId ) );
210 hash_combine( signature, std::string_view( category.name ) );
211
212 for( const HTTP_LIB_PART& part : category.cachedParts )
213 {
214 hash_combine( signature, std::string_view( part.id ) );
215 hash_combine( signature, std::string_view( part.name ) );
216 hash_combine( signature, std::string_view( part.symbolIdStr ) );
217 hash_combine( signature, part.exclude_from_bom );
218 hash_combine( signature, part.exclude_from_board );
219 hash_combine( signature, part.exclude_from_sim );
220 hash_combine( signature, std::string_view( part.desc ) );
221 hash_combine( signature, std::string_view( part.keywords ) );
222
223 for( const auto& [fieldName, fieldProps] : part.fields )
224 {
225 hash_combine( signature, std::string_view( fieldName ) );
226 hash_combine( signature, std::string_view( std::get<0>( fieldProps ) ) );
227 hash_combine( signature, std::get<1>( fieldProps ) );
228 }
229
230 for( const std::string& filter : part.fp_filters )
231 hash_combine( signature, std::string_view( filter ) );
232
233 if( !part.symbolIdStr.empty() )
234 {
235 LIB_ID symbolId;
236 symbolId.Parse( part.symbolIdStr );
237
238 if( symbolId.IsValid() && m_adapter )
239 {
240 const UTF8& nickname = symbolId.GetLibNickname();
241 hash_combine( signature, std::string_view( nickname.c_str() ) );
242
243 if( std::optional<int> libHash = m_adapter->GetLibraryModifyHash( nickname ) )
244 hash_combine( signature, *libHash );
245 }
246 }
247 }
248 }
249
250 return signature;
251}
252
253
254void SCH_IO_HTTP_LIB::materializeCache( const wxString& aLibraryPath,
255 const std::map<std::string, HTTP_LIB_CATEGORY>& aCategoryData )
256{
257 std::map<wxString, std::unique_ptr<LIB_SYMBOL>> newSymbolCache;
258 std::map<wxString, std::pair<std::string, std::string>> newPartIdMap;
259 std::set<wxString> newCustomFields;
260
261 for( const HTTP_LIB_CATEGORY& category : aCategoryData | std::views::values )
262 {
263 for( const HTTP_LIB_PART& part : category.cachedParts )
264 {
265 wxString symbolName( part.name );
266 newPartIdMap[symbolName] = { part.id, category.id };
267
268 LIB_SYMBOL* symbol = loadSymbolFromPart( aLibraryPath, symbolName, category, part, newCustomFields );
269
270 if( symbol )
271 newSymbolCache[symbolName] = std::unique_ptr<LIB_SYMBOL>( symbol );
272 }
273 }
274
275 {
276 std::unique_lock lock( m_cacheMutex );
277
278 m_symbolCache = std::move( newSymbolCache );
279 m_partIdMap = std::move( newPartIdMap );
280 m_customFields = std::move( newCustomFields );
281
282 m_cachePopulated = true;
283 m_modifyHash++;
284 }
285}
286
287
288void SCH_IO_HTTP_LIB::cacheLib( const wxString& aLibraryPath )
289{
290 if( m_inCacheLib )
291 return;
292
293 // After the initial load the background refresh thread handles all cache updates.
294 {
295 std::shared_lock lock( m_cacheMutex );
296
297 if( m_cachePopulated )
298 return;
299 }
300
301 m_inCacheLib = true;
302
303 struct CACHE_LIB_GUARD
304 {
305 bool* flag;
306 ~CACHE_LIB_GUARD() { *flag = false; }
307 } cacheLibGuard{ &m_inCacheLib };
308
309 m_libraryPath = aLibraryPath;
310
311 std::map<std::string, HTTP_LIB_CATEGORY> categoryData;
312
313 for( const HTTP_LIB_CATEGORY& category : m_conn->getCategories() )
314 {
315 std::vector<HTTP_LIB_PART> foundParts;
316
317 if( !fetchCategoryParts( *m_conn, category, foundParts ) )
318 {
319 if( !m_conn->GetLastError().empty() )
320 {
321 THROW_IO_ERROR( wxString::Format( _( "Error retrieving data from HTTP library %s: %s" ), category.name,
322 m_conn->GetLastError() ) );
323 }
324
325 continue;
326 }
327
328 HTTP_LIB_CATEGORY cached = category;
329 cached.cachedParts = std::move( foundParts );
330 categoryData[category.id] = std::move( cached );
331 }
332
333 size_t signature = computeSignature( categoryData );
334
335 {
336 std::unique_lock lock( m_cacheMutex );
337
338 if( m_cachePopulated && signature == m_cacheSignature )
339 return;
340 }
341
342 materializeCache( aLibraryPath, categoryData );
343
344 {
345 std::unique_lock lock( m_cacheMutex );
346 m_cacheSignature = signature;
347 }
348
349 if( !m_refreshRunning.load() )
351}
352
353
354void SCH_IO_HTTP_LIB::EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath,
355 const std::map<std::string, UTF8>* aProperties )
356{
357 wxCHECK_RET( m_adapter, "HTTP plugin missing library manager adapter handle!" );
358 ensureSettings( aLibraryPath );
360
361 if( !m_conn )
363
364 cacheLib( aLibraryPath );
365
366 bool powerSymbolsOnly = aProperties && aProperties->contains( SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly );
367
368 std::shared_lock lock( m_cacheMutex );
369
370 for( const auto& [name, symbol] : m_symbolCache )
371 {
372 if( !powerSymbolsOnly || symbol->IsPower() )
373 aSymbolNameList.Add( name );
374 }
375}
376
377
378void SCH_IO_HTTP_LIB::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList, const wxString& aLibraryPath,
379 const std::map<std::string, UTF8>* aProperties )
380{
381 wxCHECK_RET( m_adapter, "HTTP plugin missing library manager adapter handle!" );
382 ensureSettings( aLibraryPath );
384
385 if( !m_conn )
387
388 cacheLib( aLibraryPath );
389
390 bool powerSymbolsOnly = aProperties && aProperties->contains( SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly );
391
392 std::shared_lock lock( m_cacheMutex );
393
394 for( const std::unique_ptr<LIB_SYMBOL>& symbol : m_symbolCache | std::views::values )
395 {
396 if( !powerSymbolsOnly || symbol->IsPower() )
397 aSymbolList.emplace_back( symbol->Duplicate() );
398 }
399}
400
401
402void SCH_IO_HTTP_LIB::CheckLibrary( const wxString& aLibraryPath,
403 const std::map<std::string, UTF8>* aProperties )
404{
405 ensureSettings( aLibraryPath );
407}
408
409
410LIB_SYMBOL* SCH_IO_HTTP_LIB::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
411 const std::map<std::string, UTF8>* aProperties )
412{
413 wxCHECK_MSG( m_adapter, nullptr, "HTTP plugin missing library manager adapter handle!" );
414 ensureSettings( aLibraryPath );
416
417 if( !m_conn )
419
420 cacheLib( aLibraryPath );
421
422 {
423 std::shared_lock lock( m_cacheMutex );
424
425 if( auto it = m_symbolCache.find( aAliasName ); it != m_symbolCache.end() )
426 return it->second->Duplicate();
427 }
428
429 // Cache miss: fall back to a direct per-part fetch. In the steady state every known part
430 // is materialized, so this path only serves an uncached-but-known part.
431 std::string partId;
432 std::string categoryId;
433
434 {
435 std::shared_lock lock( m_cacheMutex );
436
437 if( auto it = m_partIdMap.find( aAliasName ); it != m_partIdMap.end() )
438 {
439 partId = it->second.first;
440 categoryId = it->second.second;
441 }
442 }
443
444 if( partId.empty() )
445 {
446 wxLogTrace( traceHTTPLib, wxT( "LoadSymbol: no cached part found for %s" ), aAliasName );
447 return nullptr;
448 }
449
450 const HTTP_LIB_CATEGORY* foundCategory = nullptr;
451
452 for( const HTTP_LIB_CATEGORY& category : m_conn->getCategories() )
453 {
454 if( category.id == categoryId )
455 {
456 foundCategory = &category;
457 break;
458 }
459 }
460
461 if( !foundCategory )
462 {
463 wxLogTrace( traceHTTPLib, wxT( "LoadSymbol: no category found for %s" ), aAliasName );
464 return nullptr;
465 }
466
468
469 if( !m_conn->SelectOne( partId, result ) )
470 {
471 wxLogTrace( traceHTTPLib, wxT( "LoadSymbol: SelectOne (%s) failed for category %s" ),
472 partId, foundCategory->name );
473 THROW_IO_ERROR( wxString::Format( _( "Error retrieving part %s from HTTP library: %s" ),
474 partId, m_conn->GetLastError() ) );
475 }
476
477 wxLogTrace( traceHTTPLib, wxT( "LoadSymbol: SelectOne (%s) found in %s" ),
478 partId, foundCategory->name );
479
480 // This transient symbol is not placed in the shared cache; collect its custom fields into a
481 // local set so the background materializer's m_customFields is never mutated off-thread.
482 std::set<wxString> transientFields;
483
484 return loadSymbolFromPart( aLibraryPath, aAliasName, *foundCategory, result, transientFields );
485}
486
487
488void SCH_IO_HTTP_LIB::GetSubLibraryNames( std::vector<wxString>& aNames )
489{
490 aNames.clear();
491
492 ensureSettings( wxEmptyString );
493 connect();
494
495 // connect() leaves m_conn null when the endpoint is unreachable so a network loss
496 // degrades to an empty result instead of a null dereference while building the tree.
497 if( !m_conn )
498 return;
499
500 std::set<wxString> categoryNames;
501
502 for( const HTTP_LIB_CATEGORY& categoryIter : m_conn->getCategories() )
503 {
504 if( categoryNames.count( categoryIter.name ) )
505 continue;
506
507 aNames.emplace_back( categoryIter.name );
508 categoryNames.insert( categoryIter.name );
509 }
510}
511
512
513wxString SCH_IO_HTTP_LIB::GetSubLibraryDescription( const wxString& aName )
514{
515 ensureSettings( wxEmptyString );
516 connect();
517
518 if( !m_conn )
519 return wxEmptyString;
520
521 return m_conn->getCategoryDescription( std::string( aName.mb_str() ) );
522}
523
524
525void SCH_IO_HTTP_LIB::GetAvailableSymbolFields( std::vector<wxString>& aNames )
526{
527 // TODO: Implement this sometime; This is currently broken...
528 std::shared_lock lock( m_cacheMutex );
529 std::copy( m_customFields.begin(), m_customFields.end(), std::back_inserter( aNames ) );
530}
531
532
533void SCH_IO_HTTP_LIB::GetDefaultSymbolFields( std::vector<wxString>& aNames )
534{
535 std::copy( m_defaultShownFields.begin(), m_defaultShownFields.end(), std::back_inserter( aNames ) );
536}
537
538
539void SCH_IO_HTTP_LIB::ensureSettings( const wxString& aSettingsPath )
540{
541 auto tryLoad =
542 [&]()
543 {
544 if( !m_settings->LoadFromFile() )
545 THROW_IO_ERRORF( _( "HTTP library settings file %s missing or invalid." ), aSettingsPath );
546
547 if( m_settings->m_Source.api_version.empty() )
548 {
549 THROW_IO_ERRORF( _( "HTTP library settings file %s is missing the API version number." ),
550 aSettingsPath );
551 }
552
553 if( m_settings->getSupportedAPIVersion() != m_settings->m_Source.api_version )
554 {
555 THROW_IO_ERRORF( _( "HTTP library settings file %s uses API version %s, but KiCad requires "
556 "version %s." ),
557 aSettingsPath,
558 m_settings->m_Source.api_version,
559 m_settings->getSupportedAPIVersion() );
560 }
561
562 if( m_settings->m_Source.root_url.empty() )
563 THROW_IO_ERRORF( _( "HTTP library settings file %s is missing the root URL." ), aSettingsPath );
564
565 // map lib source type
566 m_settings->m_Source.type = m_settings->get_HTTP_LIB_SOURCE_TYPE();
567
568 if( m_settings->m_Source.type == HTTP_LIB_SOURCE_TYPE::INVALID )
569 THROW_IO_ERRORF( _( "HTTP library settings file %s has invalid library type." ), aSettingsPath );
570
571 // make sure that the root url finishes with a forward slash
572 if( m_settings->m_Source.root_url.at( m_settings->m_Source.root_url.length() - 1 ) != '/' )
573 m_settings->m_Source.root_url += "/";
574
575 // Append api version to root URL
576 m_settings->m_Source.root_url += m_settings->m_Source.api_version + "/";
577
578 if( m_sourcePatcher )
579 m_sourcePatcher( m_settings->m_Source );
580 };
581
582 if( !m_settings && !aSettingsPath.IsEmpty() )
583 {
584 std::string path( aSettingsPath.ToUTF8() );
585 m_settings = std::make_unique<HTTP_LIB_SETTINGS>( path );
586
587 m_settings->SetReadOnly( true );
588
589 tryLoad();
590 }
591 else if( !m_conn && m_settings )
592 {
593 // If we have valid settings but no connection yet; reload settings in case user is editing
594 tryLoad();
595 }
596 else if( !m_settings )
597 {
598 wxLogTrace( traceHTTPLib, wxT( "ensureSettings: no settings available!" ) );
599 }
600}
601
602
604{
605 wxCHECK_RET( m_settings, "Call ensureSettings before ensureConnection!" );
606
607 connect();
608
609 if( !m_conn || !m_conn->IsValidEndpoint() )
610 THROW_IO_ERRORF( _( "Could not connect to %s. Errors: %s" ), m_settings->m_Source.root_url, m_lastError );
611}
612
613
615{
616 wxCHECK_RET( m_settings, "Call ensureSettings before connect()!" );
617
618 {
619 std::unique_lock connLock( m_cacheMutex );
620
621 if( !m_conn )
622 {
625 else
626 m_conn = std::make_unique<HTTP_LIB_CONNECTION>( m_settings->m_Source, true );
627
628 if( !m_conn->IsValidEndpoint() )
629 {
630 m_lastError = m_conn->GetLastError();
631
632 // Make sure we release pointer so we are able to query API again next time
633 m_conn.reset();
634 }
635 }
636 }
637}
638
639
640LIB_SYMBOL* SCH_IO_HTTP_LIB::loadSymbolFromPart( const wxString& aLibraryPath,
641 const wxString& aSymbolName,
642 const HTTP_LIB_CATEGORY& aCategory,
643 const HTTP_LIB_PART& aPart,
644 std::set<wxString>& aCustomFields )
645{
646 LIB_SYMBOL* symbol = nullptr;
647 LIB_SYMBOL* originalSymbol = nullptr;
648 LIB_ID symbolId;
649
650 std::string symbolIdStr = aPart.symbolIdStr;
651
652 // Extract library nickname from the library path (e.g., "/path/to/W5.kicad_httplib" -> "W5")
653 wxFileName libFileName( aLibraryPath );
654 wxString libNickname = libFileName.GetName();
655
656 // Get or Create the symbol using the found symbol
657 if( !symbolIdStr.empty() )
658 {
659 symbolId.Parse( symbolIdStr );
660
661 // A part's symbolIdStr may resolve back into this same HTTP library (issue #24249,
662 // e.g. a mistyped library nickname). The adapter routes that lookup back into
663 // SCH_IO_HTTP_LIB::LoadSymbol, which would re-enter loadSymbolFromPart until the stack
664 // overflows. Track in-flight LIB_IDs and skip the recursive load on re-entry.
665 struct CYCLE_GUARD
666 {
667 std::unordered_set<wxString>* set;
668 wxString key;
669 bool owns = false;
670
671 ~CYCLE_GUARD()
672 {
673 if( owns )
674 set->erase( key );
675 }
676 } guard{ &m_inProgressLoads, {}, false };
677
678 bool cycle = false;
679
680 if( symbolId.IsValid() )
681 {
682 guard.key = symbolId.Format().wx_str();
683 guard.owns = m_inProgressLoads.insert( guard.key ).second;
684 cycle = !guard.owns;
685
686 if( cycle )
687 {
688 wxLogTrace( traceHTTPLib,
689 wxT( "loadSymbolFromPart: cycle detected resolving '%s' "
690 "(part '%s'); skipping recursive load" ),
691 symbolIdStr, aSymbolName );
692 }
693 else
694 {
695 originalSymbol = m_adapter->LoadSymbol( symbolId );
696 }
697 }
698
699 if( originalSymbol )
700 {
701 wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: found original symbol '%s'" ),
702 symbolIdStr );
703
704 symbol = originalSymbol->Duplicate();
705 symbol->SetSourceLibId( symbolId );
706 symbol->SetName( aSymbolName );
707
708 LIB_ID libId = symbol->GetLibId();
709 libId.SetLibNickname( libNickname );
710 libId.SetSubLibraryName( aCategory.name );
711 symbol->SetLibId( libId );
712 }
713 else if( cycle )
714 {
715 wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: source symbol '%s' is a "
716 "self-reference, will create empty symbol" ),
717 symbolIdStr );
718 }
719 else if( !symbolId.IsValid() )
720 {
721 wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: source symbol id '%s' is invalid, "
722 "will create empty symbol" ), symbolIdStr );
723 }
724 else
725 {
726 wxLogTrace( traceHTTPLib, wxT( "loadSymbolFromPart: source symbol '%s' not found, "
727 "will create empty symbol" ), symbolIdStr );
728 }
729 }
730
731 std::lock_guard lock( m_symbolLoadMutex );
732
733 if( !symbol )
734 {
735 // Actual symbol not found: return metadata only; error will be
736 // indicated in the symbol chooser
737 symbol = new LIB_SYMBOL( aSymbolName );
738
739 LIB_ID libId = symbol->GetLibId();
740 libId.SetLibNickname( libNickname );
741 libId.SetSubLibraryName( aCategory.name );
742 symbol->SetLibId( libId );
743 }
744
745 symbol->SetExcludedFromBOM( aPart.exclude_from_bom );
747 symbol->SetExcludedFromSim( aPart.exclude_from_sim );
748
749 wxArrayString fp_filters;
750
751 for( auto& [fieldName, fieldProperties] : aPart.fields )
752 {
753 wxString lowerFieldName = wxString( fieldName ).Lower();
754
755 if( lowerFieldName == footprint_field )
756 {
757 SCH_FIELD* field = &symbol->GetFootprintField();
758 wxStringTokenizer tokenizer( std::get<0>( fieldProperties ), ";\t\r\n", wxTOKEN_STRTOK );
759
760 while( tokenizer.HasMoreTokens() )
761 fp_filters.Add( tokenizer.GetNextToken() );
762
763 if( fp_filters.size() > 0 )
764 field->SetText( fp_filters[0] );
765
766 field->SetVisible( std::get<1>( fieldProperties ) );
767 }
768 else if( lowerFieldName == description_field )
769 {
770 SCH_FIELD* field = &symbol->GetDescriptionField();
771 field->SetText( std::get<0>( fieldProperties ) );
772 field->SetVisible( std::get<1>( fieldProperties ) );
773 }
774 else if( lowerFieldName == value_field )
775 {
776 SCH_FIELD* field = &symbol->GetValueField();
777 field->SetText( std::get<0>( fieldProperties ) );
778 field->SetVisible( std::get<1>( fieldProperties ) );
779 }
780 else if( lowerFieldName == datasheet_field )
781 {
782 SCH_FIELD* field = &symbol->GetDatasheetField();
783 field->SetText( std::get<0>( fieldProperties ) );
784 field->SetVisible( std::get<1>( fieldProperties ) );
785 }
786 else if( lowerFieldName == reference_field )
787 {
788 SCH_FIELD* field = &symbol->GetReferenceField();
789 field->SetText( std::get<0>( fieldProperties ) );
790 field->SetVisible( std::get<1>( fieldProperties ) );
791 }
792 else if( lowerFieldName == keywords_field )
793 {
794 symbol->SetKeyWords( std::get<0>( fieldProperties ) );
795 }
796 else
797 {
798 // Check if field exists, if so replace Text and adjust visiblity.
799 //
800 // This proves useful in situations where, for instance, an individual requires a particular value, such as
801 // the material type showcased at a specific position for a capacitor. Subsequently, this value could be defined
802 // in the symbol itself and then, potentially, be modified by the HTTP library as necessary.
803 SCH_FIELD* field = symbol->GetField( fieldName );
804
805 if( field != nullptr )
806 {
807 // adjust values accordingly
808 field->SetText( std::get<0>( fieldProperties ) );
809 field->SetVisible( std::get<1>( fieldProperties ) );
810 }
811 else
812 {
813 // Generic fields
814 field = new SCH_FIELD( symbol, FIELD_T::USER );
815 field->SetName( fieldName );
816
817 field->SetText( std::get<0>( fieldProperties ) );
818 field->SetVisible( std::get<1>( fieldProperties ) );
819 symbol->AddField( field );
820
821 aCustomFields.insert( fieldName );
822 }
823 }
824 }
825
826 // The detail record may omit description/keywords info that the category record had
827 if( !aPart.desc.empty() )
828 symbol->SetDescription( aPart.desc );
829
830 if( !aPart.keywords.empty() )
831 symbol->SetKeyWords( aPart.keywords );
832
833 for( const std::string& filter : aPart.fp_filters )
834 fp_filters.push_back( filter );
835
836 symbol->SetFPFilters( fp_filters );
837
838 // Pin-to-pad maps (issue #2282): attach non-destructively. Prefer the spec-form named maps +
839 // associations when the payload supplies them; otherwise fall back to the legacy flat form for
840 // one release, bound to the symbol's concrete Footprint field (fp_filters may carry globs).
841 if( !aPart.named_pin_maps.IsEmpty() || !aPart.associated_footprints.empty() )
842 {
843 symbol->SetPinMaps( aPart.named_pin_maps );
845 }
846 else
847 {
848 const wxString assignedFootprint = symbol->GetFootprintField().GetText();
849
850 if( !aPart.pin_map.empty() && !assignedFootprint.IsEmpty() )
851 {
852 const wxString mapName = wxS( "HTTP Library" );
853
854 symbol->PinMaps().AddOrReplace( MakeLegacyPinMap( mapName, aPart.pin_map ) );
855
856 LIB_ID fpId;
857 fpId.Parse( assignedFootprint );
858 symbol->SetAssociatedFootprints( { { fpId, mapName } } );
859 }
860 }
861
862 return symbol;
863}
864
865void SCH_IO_HTTP_LIB::SaveSymbol( const wxString& aLibraryPath, std::unique_ptr<LIB_SYMBOL> aSymbol,
866 const std::map<std::string, UTF8>* aProperties )
867{
868 // TODO: Implement this sometime;
869}
const char * name
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
bool SelectAll(const HTTP_LIB_CATEGORY &aCategory, std::vector< HTTP_LIB_PART > &aParts)
Retrieve all parts from a specific category from the HTTP library.
bool SelectOne(const std::string &aPartID, HTTP_LIB_PART &aFetchedPart)
Retrieve a single part with full details from the HTTP library.
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()
virtual const char * what() const override
std::exception interface, returned as UTF-8
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition lib_id.cpp:65
bool IsValid() const
Check if this LID_ID is valid.
Definition lib_id.h:168
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:113
UTF8 Format() const
Definition lib_id.cpp:132
void SetSubLibraryName(const UTF8 &aName)
Definition lib_id.h:127
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
Define a library symbol object.
Definition lib_symbol.h:114
SCH_FIELD & GetDescriptionField()
Definition lib_symbol.h:463
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:190
void SetSourceLibId(const LIB_ID &aLibId)
Definition lib_symbol.h:194
SCH_FIELD & GetDatasheetField()
Definition lib_symbol.h:459
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:130
SCH_FIELD * GetField(const wxString &aFieldName)
Find a field within this symbol matching aFieldName; return nullptr if not found.
SCH_FIELD & GetFootprintField()
Definition lib_symbol.h:455
void SetAssociatedFootprints(std::vector< ASSOCIATED_FOOTPRINT > aList)
Definition lib_symbol.h:272
void SetDescription(const wxString &aDescription)
Set the Description field text value.
void SetKeyWords(const wxString &aKeyWords)
SCH_FIELD & GetValueField()
Definition lib_symbol.h:447
void SetPinMaps(const PIN_MAP_SET &aPinMaps)
Definition lib_symbol.h:268
void SetFPFilters(const wxArrayString &aFilters)
Definition lib_symbol.h:250
void SetLibId(const LIB_ID &aLibId)
void AddField(SCH_FIELD *aField)
Add a field.
PIN_MAP_SET & PinMaps()
Definition lib_symbol.h:267
virtual void SetName(const wxString &aName)
SCH_FIELD & GetReferenceField()
Definition lib_symbol.h:451
bool IsEmpty() const
Definition pin_map.h:140
void AddOrReplace(PIN_MAP aMap)
Insert aMap, replacing any existing entry with the same name.
Definition pin_map.cpp:113
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
void SetName(const wxString &aName)
void SetText(const wxString &aText) override
std::thread m_refreshThread
LIB_SYMBOL * loadSymbolFromPart(const wxString &aLibraryPath, const wxString &aSymbolName, const HTTP_LIB_CATEGORY &aCategory, const HTTP_LIB_PART &aPart, std::set< wxString > &aCustomFields)
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...
std::map< wxString, std::unique_ptr< LIB_SYMBOL > > m_symbolCache
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::map< wxString, std::pair< std::string, std::string > > m_partIdMap
Symbol name -> (part id, category id); used for the SelectOne fallback in LoadSymbol.
std::set< wxString > m_defaultShownFields
void SaveSymbol(const wxString &aLibraryPath, std::unique_ptr< LIB_SYMBOL > aSymbol, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Write aSymbol to an existing library located at aLibraryPath.
SOURCE_PATCHER m_sourcePatcher
std::unordered_set< wxString > m_inProgressLoads
std::mutex m_refreshMutex
std::mutex m_symbolLoadMutex
Serializes loadSymbolFromPart calls.
CONNECTION_BUILDER m_connectionFactory
Allows replacing actual HTTP connection for QA tests.
wxString description_field
std::atomic< bool > m_refreshRunning
void materializeCache(const wxString &aLibraryPath, const std::map< std::string, HTTP_LIB_CATEGORY > &aCategoryData)
size_t computeSignature(const std::map< std::string, HTTP_LIB_CATEGORY > &aCategoryData) const
std::atomic< bool > m_cachePopulated
void CheckLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Validate that the library at aLibraryPath is reachable and well-formed, without necessarily loading s...
std::condition_variable m_refreshCV
void cacheLib(const wxString &aLibraryPath)
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.
std::shared_mutex m_cacheMutex
Protects m_symbolCache, m_partIdMap, and m_customFields.
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:406
static const char * PropPowerSymsOnly
void SetExcludedFromBoard(bool aExclude, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
Set or clear exclude from board netlist flag.
Definition symbol.h:210
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:180
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:195
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:67
const char * c_str() const
Definition utf8.h:104
wxString wx_str() const
Definition utf8.cpp:41
#define _(s)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
static constexpr void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
Definition hash.h:28
const char *const traceHTTPLib
PIN_MAP MakeLegacyPinMap(const wxString &aName, const std::unordered_map< wxString, std::vector< wxString > > &aAssignments)
Build a single named PIN_MAP from a legacy symbol-pin to footprint-pad(s) assignment table (the flat ...
Definition pin_map.cpp:151
static bool fetchCategoryParts(HTTP_LIB_CONNECTION &aConn, const HTTP_LIB_CATEGORY &aCategory, std::vector< HTTP_LIB_PART > &aParts)
std::vector< HTTP_LIB_PART > cachedParts
std::string name
name of category
std::vector< ASSOCIATED_FOOTPRINT > associated_footprints
std::string symbolIdStr
std::unordered_map< wxString, std::vector< wxString > > pin_map
Legacy flat MR !2540 pin assignment table (read for one release; issue #2282).
std::string keywords
std::vector< std::string > fp_filters
std::vector< std::pair< std::string, field_type > > fields
PIN_MAP_SET named_pin_maps
Spec-form named pin maps and their footprint associations (issue #2282).
@ USER
The field ID hasn't been set yet; field is invalid.
std::string path
wxString result
Test unit parsing edge cases and error handling.