KiCad PCB EDA Suite
sch_database_plugin.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) 2022 Jon Evans <[email protected]>
5 * Copyright (C) 2022 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#include <iostream>
22#include <unordered_set>
23
24#include <boost/algorithm/string.hpp>
25
28#include <fmt.h>
29#include <lib_symbol.h>
30#include <symbol_lib_table.h>
31
32#include "sch_database_plugin.h"
33
34
36 m_libTable( nullptr ),
37 m_settings(),
38 m_conn()
39{
40}
41
42
44{
45}
46
47
48void SCH_DATABASE_PLUGIN::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
49 const wxString& aLibraryPath,
50 const STRING_UTF8_MAP* aProperties )
51{
52 std::vector<LIB_SYMBOL*> symbols;
53 EnumerateSymbolLib( symbols, aLibraryPath, aProperties );
54
55 for( LIB_SYMBOL* symbol : symbols )
56 aSymbolNameList.Add( symbol->GetName() );
57}
58
59
60void SCH_DATABASE_PLUGIN::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
61 const wxString& aLibraryPath,
62 const STRING_UTF8_MAP* aProperties )
63{
64 wxCHECK_RET( m_libTable, "Database plugin missing library table handle!" );
65 ensureSettings( aLibraryPath );
67
68 bool powerSymbolsOnly = ( aProperties &&
69 aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly ) !=
70 aProperties->end() );
71
72 for( const DATABASE_LIB_TABLE& table : m_settings->m_Tables )
73 {
74 std::vector<DATABASE_CONNECTION::ROW> results;
75
76 if( !m_conn->SelectAll( table.table, results ) )
77 {
78 if( !m_conn->GetLastError().empty() )
79 {
80 wxString msg = wxString::Format( _( "Error reading database table %s: %s" ),
81 table.table, m_conn->GetLastError() );
82 THROW_IO_ERROR( msg );
83 }
84
85 continue;
86 }
87
88 for( DATABASE_CONNECTION::ROW& result : results )
89 {
90 if( !result.count( table.key_col ) )
91 continue;
92
93 std::string prefix = table.name.empty() ? "" : fmt::format( "{}/", table.name );
94 wxString name( fmt::format( "{}{}", prefix,
95 std::any_cast<std::string>( result[table.key_col] ) ) );
96
97 LIB_SYMBOL* symbol = loadSymbolFromRow( name, table, result );
98
99 if( symbol && ( !powerSymbolsOnly || symbol->IsPower() ) )
100 aSymbolList.emplace_back( symbol );
101 }
102 }
103}
104
105
106LIB_SYMBOL* SCH_DATABASE_PLUGIN::LoadSymbol( const wxString& aLibraryPath,
107 const wxString& aAliasName,
108 const STRING_UTF8_MAP* aProperties )
109{
110 wxCHECK( m_libTable, nullptr );
111 ensureSettings( aLibraryPath );
113
114 /*
115 * Table names are tricky, in order to allow maximum flexibility to the user.
116 * The slash character is used as a separator between a table name and symbol name, but symbol
117 * names may also contain slashes and table names may now also be empty (which results in the
118 * slash being dropped in the symbol name when placing a new symbol). So, if a slash is found,
119 * we check if the string before the slash is a valid table name. If not, we assume the table
120 * name is blank if our config has an entry for the null table.
121 */
122
123 std::string tableName = "";
124 std::string symbolName( aAliasName.ToUTF8() );
125
126 if( aAliasName.Contains( '/' ) )
127 {
128 tableName = std::string( aAliasName.BeforeFirst( '/' ).ToUTF8() );
129 symbolName = std::string( aAliasName.AfterFirst( '/' ).ToUTF8() );
130 }
131
132 std::vector<const DATABASE_LIB_TABLE*> tablesToTry;
133
134 for( const DATABASE_LIB_TABLE& tableIter : m_settings->m_Tables )
135 {
136 if( tableIter.name == tableName )
137 tablesToTry.emplace_back( &tableIter );
138 }
139
140 if( tablesToTry.empty() )
141 {
142 wxLogTrace( traceDatabase, wxT( "LoadSymbol: table '%s' not found in config" ), tableName );
143 return nullptr;
144 }
145
146 const DATABASE_LIB_TABLE* foundTable = nullptr;
148
149 for( const DATABASE_LIB_TABLE* table : tablesToTry )
150 {
151 if( m_conn->SelectOne( table->table, std::make_pair( table->key_col, symbolName ),
152 result ) )
153 {
154 foundTable = table;
155 wxLogTrace( traceDatabase, wxT( "LoadSymbol: SelectOne (%s, %s) found in %s" ),
156 table->key_col, symbolName, table->table );
157 }
158 else
159 {
160 wxLogTrace( traceDatabase, wxT( "LoadSymbol: SelectOne (%s, %s) failed for table %s" ),
161 table->key_col, symbolName, table->table );
162 }
163 }
164
165 wxCHECK( foundTable, nullptr );
166
167 return loadSymbolFromRow( aAliasName, *foundTable, result );
168}
169
170
171void SCH_DATABASE_PLUGIN::GetSubLibraryNames( std::vector<wxString>& aNames )
172{
173 ensureSettings( wxEmptyString );
174
175 aNames.clear();
176
177 std::set<wxString> tableNames;
178
179 for( const DATABASE_LIB_TABLE& tableIter : m_settings->m_Tables )
180 {
181 if( tableNames.count( tableIter.name ) )
182 continue;
183
184 aNames.emplace_back( tableIter.name );
185 tableNames.insert( tableIter.name );
186 }
187}
188
189
190void SCH_DATABASE_PLUGIN::GetAvailableSymbolFields( std::vector<wxString>& aNames )
191{
192 std::copy( m_customFields.begin(), m_customFields.end(), std::back_inserter( aNames ) );
193}
194
195
196void SCH_DATABASE_PLUGIN::GetDefaultSymbolFields( std::vector<wxString>& aNames )
197{
198 std::copy( m_defaultShownFields.begin(), m_defaultShownFields.end(),
199 std::back_inserter( aNames ) );
200}
201
202
203bool SCH_DATABASE_PLUGIN::CheckHeader( const wxString& aFileName )
204{
205 // TODO: Implement this sometime; but CheckHeader isn't even called...
206 return true;
207}
208
209
210void SCH_DATABASE_PLUGIN::ensureSettings( const wxString& aSettingsPath )
211{
212 auto tryLoad =
213 [&]()
214 {
215 if( !m_settings->LoadFromFile() )
216 {
217 wxString msg = wxString::Format(
218 _( "Could not load database library: settings file %s missing or invalid" ),
219 aSettingsPath );
220
221 THROW_IO_ERROR( msg );
222 }
223 };
224
225 if( !m_settings && !aSettingsPath.IsEmpty() )
226 {
227 std::string path( aSettingsPath.ToUTF8() );
228 m_settings = std::make_unique<DATABASE_LIB_SETTINGS>( path );
229 m_settings->SetReadOnly( true );
230
231 tryLoad();
232 }
233 else if( !m_conn && m_settings )
234 {
235 // If we have valid settings but no connection yet; reload settings in case user is editing
236 tryLoad();
237 }
238 else if( m_conn && m_settings && !aSettingsPath.IsEmpty() )
239 {
240 wxASSERT_MSG( aSettingsPath == m_settings->GetFilename(),
241 "Path changed for database library without re-initializing plugin!" );
242 }
243 else if( !m_settings )
244 {
245 wxLogTrace( traceDatabase, wxT( "ensureSettings: no settings but no valid path!" ) );
246 }
247}
248
249
251{
252 wxCHECK_RET( m_settings, "Call ensureSettings before ensureConnection!" );
253
254 if( m_conn && !m_conn->IsConnected() )
255 m_conn.reset();
256
257 if( !m_conn )
258 {
259 if( m_settings->m_Source.connection_string.empty() )
260 {
261 m_conn = std::make_unique<DATABASE_CONNECTION>( m_settings->m_Source.dsn,
262 m_settings->m_Source.username,
263 m_settings->m_Source.password,
264 m_settings->m_Source.timeout );
265 }
266 else
267 {
268 std::string cs = m_settings->m_Source.connection_string;
269 std::string basePath( wxFileName( m_settings->GetFilename() ).GetPath().ToUTF8() );
270
271 // Database drivers that use files operate on absolute paths, so provide a mechanism
272 // for specifing on-disk databases that live next to the kicad_dbl file
273 boost::replace_all( cs, "${CWD}", basePath );
274
275 m_conn = std::make_unique<DATABASE_CONNECTION>( cs, m_settings->m_Source.timeout );
276 }
277
278 if( !m_conn->IsConnected() )
279 {
280 wxString msg = wxString::Format(
281 _( "Could not load database library: could not connect to database %s (%s)" ),
282 m_settings->m_Source.dsn,
283 m_conn->GetLastError() );
284
285 m_conn.reset();
286
287 THROW_IO_ERROR( msg );
288 }
289
290 m_conn->SetCacheParams( m_settings->m_Cache.max_size, m_settings->m_Cache.max_age );
291 }
292}
293
294
296 const DATABASE_LIB_TABLE& aTable,
297 const DATABASE_CONNECTION::ROW& aRow )
298{
299 LIB_SYMBOL* symbol = nullptr;
300
301 if( aRow.count( aTable.symbols_col ) )
302 {
303 // TODO: Support multiple options for symbol
304 std::string symbolIdStr = std::any_cast<std::string>( aRow.at( aTable.symbols_col ) );
305 LIB_ID symbolId;
306 symbolId.Parse( std::any_cast<std::string>( aRow.at( aTable.symbols_col ) ) );
307
308 LIB_SYMBOL* originalSymbol = m_libTable->LoadSymbol( symbolId );
309
310 if( originalSymbol )
311 {
312 wxLogTrace( traceDatabase, wxT( "loadSymbolFromRow: found original symbol '%s'" ),
313 symbolIdStr );
314 symbol = originalSymbol->Duplicate();
315 symbol->SetSourceLibId( symbolId );
316 }
317 else
318 {
319 wxLogTrace( traceDatabase, wxT( "loadSymboFromRow: source symbol '%s' not found, "
320 "will create empty symbol" ), symbolIdStr );
321 }
322 }
323
324 if( !symbol )
325 {
326 // Actual symbol not found: return metadata only; error will be indicated in the
327 // symbol chooser
328 symbol = new LIB_SYMBOL( aSymbolName );
329 }
330 else
331 {
332 symbol->SetName( aSymbolName );
333 }
334
335 symbol->LibId().SetSubLibraryName( aTable.name );
336
337 if( aRow.count( aTable.footprints_col ) )
338 {
339 // TODO: Support multiple footprint choices
340 std::string footprints = std::any_cast<std::string>( aRow.at( aTable.footprints_col ) );
341 wxString footprint = wxString( footprints.c_str(), wxConvUTF8 ).BeforeFirst( ';' );
342 symbol->GetFootprintField().SetText( footprint );
343 }
344 else
345 {
346 wxLogTrace( traceDatabase, wxT( "loadSymboFromRow: footprint field %s not found." ),
347 aTable.footprints_col );
348 }
349
350 if( !aTable.properties.description.empty() && aRow.count( aTable.properties.description ) )
351 {
352 wxString value(
353 std::any_cast<std::string>( aRow.at( aTable.properties.description ) ).c_str(),
354 wxConvUTF8 );
355 symbol->SetDescription( value );
356 }
357
358 if( !aTable.properties.keywords.empty() && aRow.count( aTable.properties.keywords ) )
359 {
360 wxString value( std::any_cast<std::string>( aRow.at( aTable.properties.keywords ) ).c_str(),
361 wxConvUTF8 );
362 symbol->SetKeyWords( value );
363 }
364
365 if( !aTable.properties.footprint_filters.empty()
366 && aRow.count( aTable.properties.footprint_filters ) )
367 {
368 wxString value( std::any_cast<std::string>( aRow.at( aTable.properties.footprint_filters ) )
369 .c_str(),
370 wxConvUTF8 );
371 wxArrayString filters;
372 filters.push_back( value );
373 symbol->SetFPFilters( filters );
374 }
375
376 if( !aTable.properties.exclude_from_board.empty()
377 && aRow.count( aTable.properties.exclude_from_board ) )
378 {
379 bool exclude = std::any_cast<bool>( aRow.at( aTable.properties.exclude_from_board ) );
380 symbol->SetIncludeOnBoard( !exclude );
381 }
382
383 if( !aTable.properties.exclude_from_bom.empty()
384 && aRow.count( aTable.properties.exclude_from_bom ) )
385 {
386 bool exclude = std::any_cast<bool>( aRow.at( aTable.properties.exclude_from_bom ) );
387 symbol->SetIncludeInBom( !exclude );
388 }
389
390 std::vector<LIB_FIELD*> fields;
391 symbol->GetFields( fields );
392
393 std::unordered_map<wxString, LIB_FIELD*> fieldsMap;
394
395 for( LIB_FIELD* field : fields )
396 fieldsMap[field->GetName()] = field;
397
398 for( const DATABASE_FIELD_MAPPING& mapping : aTable.fields )
399 {
400 if( !aRow.count( mapping.column ) )
401 {
402 wxLogTrace( traceDatabase, wxT( "loadSymboFromRow: field %s not found in result" ),
403 mapping.column );
404 continue;
405 }
406
407 wxString value( std::any_cast<std::string>( aRow.at( mapping.column ) ).c_str(),
408 wxConvUTF8 );
409
410 if( mapping.name == wxT( "Value" ) )
411 {
412 LIB_FIELD& field = symbol->GetValueField();
413 field.SetText( value );
414
415 if( !mapping.inherit_properties )
416 {
417 field.SetVisible( mapping.visible_on_add );
418 field.SetNameShown( mapping.show_name );
419 }
420 continue;
421 }
422 else if( mapping.name == wxT( "Datasheet" ) )
423 {
424 LIB_FIELD& field = symbol->GetDatasheetField();
425 field.SetText( value );
426
427 if( !mapping.inherit_properties )
428 {
429 field.SetVisible( mapping.visible_on_add );
430 field.SetNameShown( mapping.show_name );
431
432 if( mapping.visible_on_add )
433 field.SetAutoAdded( true );
434 }
435
436 continue;
437 }
438
439 LIB_FIELD* field;
440 bool isNew = false;
441
442 if( fieldsMap.count( mapping.name ) )
443 {
444 field = fieldsMap[mapping.name];
445 }
446 else
447 {
448 field = new LIB_FIELD( symbol->GetNextAvailableFieldId() );
449 field->SetName( mapping.name );
450 isNew = true;
451 fieldsMap[mapping.name] = field;
452 }
453
454 if( !mapping.inherit_properties || isNew )
455 {
456 field->SetVisible( mapping.visible_on_add );
457 field->SetAutoAdded( true );
458 field->SetNameShown( mapping.show_name );
459 }
460
461 field->SetText( value );
462
463 if( isNew )
464 symbol->AddField( field );
465
466 m_customFields.insert( mapping.name );
467
468 if( mapping.visible_in_chooser )
469 m_defaultShownFields.insert( mapping.name );
470 }
471
472 return symbol;
473}
const char * name
Definition: DXF_plotter.cpp:56
std::map< std::string, std::any > ROW
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:217
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:163
Field object used in symbol libraries.
Definition: lib_field.h:61
void SetAutoAdded(bool aAutoAdded)
Definition: lib_field.h:178
void SetName(const wxString &aName)
Set a user definable field name to aName.
Definition: lib_field.cpp:494
void SetNameShown(bool aShown=true)
Definition: lib_field.h:181
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:50
void SetSubLibraryName(const UTF8 &aName)
Definition: lib_id.h:131
Define a library symbol object.
Definition: lib_symbol.h:99
void SetIncludeOnBoard(bool aIncludeOnBoard)
Set or clear include in board netlist flag.
Definition: lib_symbol.h:648
void SetSourceLibId(const LIB_ID &aLibId)
Definition: lib_symbol.h:145
int GetNextAvailableFieldId() const
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:115
bool IsPower() const
Definition: lib_symbol.cpp:548
LIB_FIELD & GetFootprintField()
Return reference to the footprint field.
void SetDescription(const wxString &aDescription)
Definition: lib_symbol.h:149
void SetKeyWords(const wxString &aKeyWords)
Definition: lib_symbol.h:162
void GetFields(std::vector< LIB_FIELD * > &aList)
Return a list of fields within this symbol.
LIB_FIELD & GetValueField()
Return reference to the value field.
void SetFPFilters(const wxArrayString &aFilters)
Definition: lib_symbol.h:197
void AddField(LIB_FIELD *aField)
Add a field.
LIB_ID & LibId()
Definition: lib_symbol.h:140
LIB_FIELD & GetDatasheetField()
Return reference to the datasheet field.
void SetIncludeInBom(bool aIncludeInBom)
Set or clear the include in schematic bill of materials flag.
Definition: lib_symbol.h:640
virtual void SetName(const wxString &aName)
Definition: lib_symbol.cpp:437
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< DATABASE_CONNECTION > m_conn
void GetSubLibraryNames(std::vector< wxString > &aNames) override
Retrieves a list of sub-libraries in this library.
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const STRING_UTF8_MAP *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
void ensureSettings(const wxString &aSettingsPath)
bool CheckHeader(const wxString &aFileName) override
Return true if the first line in aFileName begins with the expected header.
std::set< wxString > m_defaultShownFields
std::set< wxString > m_customFields
LIB_SYMBOL * loadSymbolFromRow(const wxString &aSymbolName, const DATABASE_LIB_TABLE &aTable, const DATABASE_CONNECTION::ROW &aRow)
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const STRING_UTF8_MAP *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
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...
std::unique_ptr< DATABASE_LIB_SETTINGS > m_settings
SYMBOL_LIB_TABLE * m_libTable
A name/value tuple with unique names and optional values.
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.
const char *const traceDatabase
#define _(s)
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
bool visible_in_chooser
Whether the column is shown by default in the chooser.
std::string column
Database column name.
std::string name
KiCad field 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.
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