23#include <unordered_set>
25#include <wx/datetime.h>
27#include <wx/tokenzr.h>
29#include <boost/algorithm/string.hpp>
47 SCH_IO( wxS(
"Database library" ) ),
64 const wxString& aLibraryPath,
65 const std::map<std::string, UTF8>* aProperties )
67 std::vector<LIB_SYMBOL*> symbols;
71 aSymbolNameList.Add( symbol->GetName() );
76 const wxString& aLibraryPath,
77 const std::map<std::string, UTF8>* aProperties )
79 wxCHECK_RET(
m_adapter,
"Database plugin missing library manager adapter handle!" );
93 if( !powerSymbolsOnly || symbol->
IsPower() )
94 aSymbolList.emplace_back( symbol );
100 const wxString& aAliasName,
101 const std::map<std::string, UTF8>* aProperties )
103 wxCHECK_MSG(
m_adapter,
nullptr,
"Database plugin missing library manager adapter handle!" );
121 std::string tableName;
122 std::string symbolName( aAliasName.ToUTF8() );
128 tableName = sanitizedIt->second.first;
129 symbolName = sanitizedIt->second.second;
135 if( aAliasName.Contains(
'/' ) )
137 tableName = std::string( aAliasName.BeforeFirst(
'/' ).ToUTF8() );
138 symbolName = std::string( aAliasName.AfterFirst(
'/' ).ToUTF8() );
142 std::vector<const DATABASE_LIB_TABLE*> tablesToTry;
147 if( tableName.empty() || tableIter.
name == tableName )
148 tablesToTry.emplace_back( &tableIter );
151 if( tablesToTry.empty() )
153 wxLogTrace(
traceDatabase, wxT(
"LoadSymbol: table '%s' not found in config" ), tableName );
165 wxLogTrace(
traceDatabase, wxT(
"LoadSymbol: SelectOne (%s, %s) found in %s" ),
170 wxLogTrace(
traceDatabase, wxT(
"LoadSymbol: SelectOne (%s, %s) failed for table %s" ),
188 std::set<wxString> tableNames;
192 if( tableNames.count( tableIter.
name ) )
195 aNames.emplace_back( tableIter.
name );
196 tableNames.insert( tableIter.
name );
220 if( aErrorMsg && ( !
m_conn || !
m_conn->IsConnected() ) )
235 long long currentTimestampSeconds = wxDateTime::Now().GetValue().GetValue() / 1000;
248 struct CACHE_LIB_GUARD
251 ~CACHE_LIB_GUARD() { *
flag =
false; }
257 std::vector<std::pair<const DATABASE_LIB_TABLE*, std::vector<DATABASE_CONNECTION::ROW>>> tableResults;
258 size_t signature = 0;
262 std::vector<DATABASE_CONNECTION::ROW> results;
266 if( !
m_conn->GetLastError().empty() )
276 for(
const auto& [column, value] :
result )
280 if(
const std::string* str = std::any_cast<std::string>( &value ) )
291 if(
const std::string* str = std::any_cast<std::string>( &it->second ) )
294 symbolId.
Parse( *str );
301 if( std::optional<int> libHash =
m_adapter->GetLibraryModifyHash( nickname ) )
308 tableResults.emplace_back( &
table, std::move( results ) );
318 std::map<wxString, std::unique_ptr<LIB_SYMBOL>> newSymbolCache;
319 std::map<wxString, std::pair<std::string, std::string>> newSanitizedNameMap;
321 for(
const auto& [
table, results] : tableResults )
328 std::string rawName = std::any_cast<std::string>(
result.at(
table->key_col ) );
330 std::string sanitizedKey = sanitizedName.
c_str();
331 std::string prefix = (
m_settings->m_GloballyUniqueKeys ||
table->name.empty() ) ?
""
332 : fmt::format(
"{}/",
table->name );
333 std::string sanitizedDisplayName = fmt::format(
"{}{}", prefix, sanitizedKey );
334 wxString
name( sanitizedDisplayName );
336 newSanitizedNameMap[
name] = std::make_pair(
table->name, rawName );
341 newSymbolCache[symbol->GetName()] = std::move( symbol );
360 THROW_IO_ERRORF(
_(
"Could not load database library: settings file %s missing or invalid" ),
367 std::string
path( aSettingsPath.ToUTF8() );
380 wxASSERT_MSG( aSettingsPath ==
m_settings->GetFilename(),
381 "Path changed for database library without re-initializing plugin!" );
385 wxLogTrace(
traceDatabase, wxT(
"ensureSettings: no settings but no valid path!" ) );
392 wxCHECK_RET(
m_settings,
"Call ensureSettings before ensureConnection!" );
398 THROW_IO_ERRORF(
_(
"Could not load database library: could not connect to database %s (%s)" ),
406 wxCHECK_RET(
m_settings,
"Call ensureSettings before connect()!" );
413 if(
m_settings->m_Source.connection_string.empty() )
422 std::string cs =
m_settings->m_Source.connection_string;
423 std::string basePath( wxFileName(
m_settings->GetFilename() ).GetPath().ToUTF8() );
427 boost::replace_all( cs,
"${CWD}", basePath );
429 m_conn = std::make_unique<DATABASE_CONNECTION>( cs,
m_settings->m_Source.timeout );
432 if( !
m_conn->IsConnected() )
441 std::set<std::string> columns;
443 columns.insert( boost::to_lower_copy( tableIter.
key_col ) );
444 columns.insert( boost::to_lower_copy( tableIter.
footprints_col ) );
445 columns.insert( boost::to_lower_copy( tableIter.
symbols_col ) );
455 columns.insert( boost::to_lower_copy( field.
column ) );
457 m_conn->CacheTableInfo( tableIter.
table, columns );
469 bool val = std::any_cast<bool>( aVal );
472 catch(
const std::bad_any_cast& )
478 int val = std::any_cast<int>( aVal );
479 return static_cast<bool>( val );
481 catch(
const std::bad_any_cast& )
487 wxString strval( std::any_cast<std::string>( aVal ).c_str(), wxConvUTF8 );
489 if( strval.IsEmpty() )
494 for(
const auto& trueVal : { wxS(
"true" ), wxS(
"yes" ), wxS(
"y" ), wxS(
"1" ) } )
496 if( strval.Matches( trueVal ) )
500 for(
const auto& falseVal : { wxS(
"false" ), wxS(
"no" ), wxS(
"n" ), wxS(
"0" ) } )
502 if( strval.Matches( falseVal ) )
506 catch(
const std::bad_any_cast& )
518 std::unique_ptr<LIB_SYMBOL> symbol =
nullptr;
525 std::string symbolIdStr = std::any_cast<std::string>( aRow.at( aTable.
symbols_col ) );
527 symbolId.
Parse( std::any_cast<std::string>( aRow.at( aTable.
symbols_col ) ) );
535 std::unordered_set<wxString>* set;
556 wxLogTrace(
traceDatabase, wxT(
"loadSymbolFromRow: cycle detected resolving '%s' "
557 "(row '%s' in table '%s'); skipping recursive load" ),
558 symbolIdStr, aSymbolName, aTable.
name );
562 originalSymbol =
m_adapter->LoadSymbol( symbolId );
568 wxLogTrace(
traceDatabase, wxT(
"loadSymbolFromRow: found original symbol '%s'" ), symbolIdStr );
569 symbol.reset( originalSymbol->
Duplicate() );
570 symbol->SetSourceLibId( symbolId );
574 wxLogTrace(
traceDatabase, wxT(
"loadSymbolFromRow: source symbol '%s' is a self-reference, "
575 "will create empty symbol" ), symbolIdStr );
579 wxLogTrace(
traceDatabase, wxT(
"loadSymboFromRow: source symbol id '%s' is invalid, "
580 "will create empty symbol" ), symbolIdStr );
584 wxLogTrace(
traceDatabase, wxT(
"loadSymboFromRow: source symbol '%s' not found, "
585 "will create empty symbol" ), symbolIdStr );
593 symbol.reset(
new LIB_SYMBOL( aSymbolName ) );
597 symbol->SetName( aSymbolName );
600 LIB_ID libId = symbol->GetLibId();
602 symbol->SetLibId( libId );
604 wxArrayString footprintsList;
608 std::string footprints = std::any_cast<std::string>( aRow.at( aTable.
footprints_col ) );
610 wxString footprintsStr = wxString( footprints.c_str(), wxConvUTF8 );
611 wxStringTokenizer tokenizer( footprintsStr,
";\t\r\n", wxTOKEN_STRTOK );
613 while( tokenizer.HasMoreTokens() )
614 footprintsList.Add( tokenizer.GetNextToken() );
616 if( footprintsList.size() > 0 )
617 symbol->GetFootprintField().SetText( footprintsList[0] );
631 std::string jsonStr = std::any_cast<std::string>( aRow.at( aTable.
pins_col ) );
633 if( !jsonStr.empty() )
635 nlohmann::json
json = nlohmann::json::parse( jsonStr );
637 if(
json.is_object() &&
json.contains(
"pin_maps" ) )
642 else if( !footprintsList.IsEmpty() )
646 if( !assignments.empty() )
648 const wxString mapName = wxS(
"Database" );
654 std::vector<ASSOCIATED_FOOTPRINT> associations;
656 for(
const wxString& footprint : footprintsList )
659 fpId.
Parse( footprint );
660 associations.push_back( { fpId, mapName } );
663 symbol->SetAssociatedFootprints( std::move( associations ) );
668 catch(
const std::exception& e )
672 Report( wxString::Format(
_(
"Error parsing pin map for database symbol '%s': %s" ),
673 aSymbolName, e.what() ),
683 symbol->SetDescription( value );
688 wxString value( std::any_cast<std::string>( aRow.at( aTable.
properties.
keywords ) ).c_str(),
690 symbol->SetKeyWords( value );
699 footprintsList.push_back( value );
702 symbol->SetFPFilters( footprintsList );
711 symbol->SetExcludedFromSim( *val );
715 wxLogTrace(
traceDatabase, wxT(
"loadSymbolFromRow: exclude_from_sim value for %s "
716 "could not be cast to a boolean" ), aSymbolName );
727 symbol->SetExcludedFromBoard( *val );
731 wxLogTrace(
traceDatabase, wxT(
"loadSymbolFromRow: exclude_from_board value for %s "
732 "could not be cast to a boolean" ), aSymbolName );
743 symbol->SetExcludedFromBOM( *val );
747 wxLogTrace(
traceDatabase, wxT(
"loadSymbolFromRow: exclude_from_bom value for %s "
748 "could not be cast to a boolean" ), aSymbolName );
752 std::vector<SCH_FIELD*> fields;
753 symbol->GetFields( fields );
755 std::unordered_map<wxString, SCH_FIELD*> fieldsMap;
758 fieldsMap[field->GetName()] = field;
760 static const wxString c_valueFieldName( wxS(
"Value" ) );
761 static const wxString c_datasheetFieldName( wxS(
"Datasheet" ) );
762 static const wxString c_footprintFieldName( wxS(
"Footprint" ) );
768 int dbFieldOrdinal = symbol->GetNextFieldOrdinal();
772 if( !aRow.count( mapping.
column ) )
774 wxLogTrace(
traceDatabase, wxT(
"loadSymbolFromRow: field %s not found in result" ), mapping.
column );
787 strValue = std::any_cast<std::string>( aRow.at( mapping.
column ) );
789 catch( std::bad_any_cast& )
793 wxString value(
strValue.c_str(), wxConvUTF8 );
795 if( mapping.
name_wx == c_valueFieldName )
797 SCH_FIELD& field = symbol->GetValueField();
808 else if( mapping.
name_wx == c_datasheetFieldName )
810 SCH_FIELD& field = symbol->GetDatasheetField();
828 if( fieldsMap.count( mapping.
name_wx ) )
830 field = fieldsMap[mapping.
name_wx];
837 fieldsMap[mapping.
name_wx] = field;
859 symbol->AddDrawItem( field,
false );
867 symbol->GetDrawItems().sort();
872 symbol->RefreshLibraryTreeCaches();
std::map< std::string, std::any > ROW
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
virtual void SetVisible(bool aVisible)
virtual void Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) const
A logical library item identifier and consists of various portions much like a URI.
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
bool IsValid() const
Check if this LID_ID is valid.
void SetSubLibraryName(const UTF8 &aName)
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Define a library symbol object.
bool IsPower() const override
virtual LIB_SYMBOL * Duplicate() const
Create a copy of a LIB_SYMBOL and assigns unique KIIDs to the copy and its children.
void SetOrdinal(int aOrdinal)
void SetAutoAdded(bool aAutoAdded)
void SetName(const wxString &aName)
void SetText(const wxString &aText) override
void SetNameShown(bool aShown=true)
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::unordered_set< wxString > m_inProgressLoads
LIB_IDs whose resolution is in flight, used to break self-referential cycles where a row's Symbols co...
std::unique_ptr< DATABASE_CONNECTION > m_conn
Generally will be null if no valid connection is established.
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 ensureSettings(const wxString &aSettingsPath)
bool m_cachePopulated
True once the LIB_SYMBOL cache has been materialized at least once.
bool TestConnection(wxString *aErrorMsg=nullptr)
bool m_inCacheLib
Re-entrancy guard for cacheLib(), tripped when a self-referential load routes back through the adapte...
std::map< wxString, std::unique_ptr< LIB_SYMBOL > > m_nameToSymbolcache
std::map< wxString, std::pair< std::string, std::string > > m_sanitizedNameMap
std::set< wxString > m_defaultShownFields
virtual ~SCH_IO_DATABASE()
size_t m_cacheSignature
Signature of the raw database rows at last materialization; used to skip rebuilding the LIB_SYMBOL ca...
void GetSubLibraryNames(std::vector< wxString > &aNames) override
Retrieves a list of sub-libraries in this library.
std::unique_ptr< DATABASE_LIB_SETTINGS > m_settings
static std::optional< bool > boolFromAny(const std::any &aVal)
SYMBOL_LIBRARY_ADAPTER * m_adapter
long long m_cacheTimestamp
DIALOG_SHIM * CreateConfigurationDialog(wxWindow *aParent) override
void GetAvailableSymbolFields(std::vector< wxString > &aNames) override
Retrieves a list of (custom) field names that are present on symbols in this library.
std::unique_ptr< LIB_SYMBOL > loadSymbolFromRow(const wxString &aSymbolName, const DATABASE_LIB_TABLE &aTable, const DATABASE_CONNECTION::ROW &aRow)
std::set< wxString > m_customFields
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...
SCH_IO(const wxString &aName)
static const char * PropPowerSymsOnly
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
const char * c_str() const
const char *const traceDatabase
static constexpr void hash_combine(std::size_t &seed)
This is a dummy function to take the final case of hash_combine below.
#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 ...
std::vector< ASSOCIATED_FOOTPRINT > ParseAssociatedFootprints(const nlohmann::json &aParent)
Parse the spec-form footprint associations from aParent["associated_footprints"] (issue #2282).
std::unordered_map< wxString, std::vector< wxString > > ParseLegacyPinAssignments(const nlohmann::json &aArray)
Parse the legacy flat pin-assignment JSON array (MR !2540 form) into a symbol-pin to footprint-pad(s)...
PIN_MAP_SET ParsePinMapSet(const nlohmann::json &aParent)
Parse the spec-form named maps from aParent["pin_maps"] into a PIN_MAP_SET (issue #2282).
static std::string strValue(double aValue)
bool visible_in_chooser
Whether the column is shown by default in the chooser.
std::string column
Database column name.
bool inherit_properties
Whether or not to inherit properties from symbol field.
bool visible_on_add
Whether to show the field when placing the symbol.
bool show_name
Whether or not to show the field name as well as its value.
wxString name_wx
KiCad field name (converted)
A database library table will be mapped to a sub-library provided by the database library entry in th...
std::string key_col
Unique key column name (will form part of the LIB_ID)
std::string name
KiCad library nickname (will form part of the LIB_ID)
std::string symbols_col
Column name containing KiCad symbol refs.
std::string footprints_col
Column name containing KiCad footprint refs.
std::vector< DATABASE_FIELD_MAPPING > fields
std::string table
Database table to pull content from.
MAPPABLE_SYMBOL_PROPERTIES properties
std::string pins_col
Column name containing JSON pin assignments (optional)
std::string footprint_filters
std::string exclude_from_sim
std::string exclude_from_board
std::string exclude_from_bom
@ USER
The field ID hasn't been set yet; field is invalid.
wxString result
Test unit parsing edge cases and error handling.