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