KiCad PCB EDA Suite
Loading...
Searching...
No Matches
footprint_wizard.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) 2026 Jon Evans <[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
22#include <api/api_utils.h>
23#include <footprint.h>
24#include <pgm_base.h>
25#include <reporter.h>
26#include <wx/log.h>
27
28#include <google/protobuf/util/json_util.h>
29
30#include "footprint_wizard.h"
31
32
34{
35 for( const std::unique_ptr<WIZARD_PARAMETER>& param : m_info.parameters )
36 param->Reset();
37}
38
39
40void FOOTPRINT_WIZARD_MANAGER::ReloadWizards( std::shared_ptr<REPORTER> aReporter )
41{
42 m_wizards.clear();
43
45 std::vector<const PLUGIN_ACTION*> actions = manager.GetActionsForScope( PLUGIN_ACTION_SCOPE::FOOTPRINT_WIZARD );
46
47 for( const PLUGIN_ACTION* action : actions )
48 {
49 std::unique_ptr<FOOTPRINT_WIZARD> wizard = std::make_unique<FOOTPRINT_WIZARD>();
50 wizard->SetIdentifier( action->identifier );
51
52 if( RefreshInfo( wizard.get(), aReporter ) )
53 m_wizards[wizard->Identifier()] = std::move( wizard );
54 }
55}
56
57
58std::vector<FOOTPRINT_WIZARD*> FOOTPRINT_WIZARD_MANAGER::Wizards() const
59{
60 std::vector<FOOTPRINT_WIZARD*> wizards;
61
62 for( const std::unique_ptr<FOOTPRINT_WIZARD>& wizard : m_wizards | std::views::values )
63 wizards.emplace_back( wizard.get() );
64
65 std::ranges::sort( wizards,
66 []( FOOTPRINT_WIZARD* const& lhs,
67 FOOTPRINT_WIZARD* const& rhs ) -> bool
68 {
69 if( !lhs || !rhs )
70 return false;
71
72 return lhs->Info().meta.name < rhs->Info().meta.name;
73 } );
74
75 return wizards;
76}
77
78
79std::optional<FOOTPRINT_WIZARD*> FOOTPRINT_WIZARD_MANAGER::GetWizard( const wxString& aIdentifier )
80{
81 if( m_wizards.contains( aIdentifier ) )
82 return m_wizards[aIdentifier].get();
83
84 return std::nullopt;
85}
86
87
88bool FOOTPRINT_WIZARD_MANAGER::RefreshInfo( FOOTPRINT_WIZARD* aWizard, std::shared_ptr<REPORTER> aReporter )
89{
90 wxCHECK( aWizard, false );
91
93
94 const wxLanguageInfo* lang = wxLocale::GetLanguageInfo( Pgm().GetSelectedLanguageIdentifier() );
95
96 std::vector<wxString> args = {
97 wxS( "--get-info" ),
98 wxS( "--lang" ),
99 lang ? lang->CanonicalName : wxString( wxS( "en" ) )
100 };
101
102 wxString out, err;
103 int ret = manager.InvokeActionSync( aWizard->Identifier(), args, &out, &err, aReporter );
104
105 if( ret != 0 )
106 return false;
107
108 kiapi::common::types::WizardInfo info;
109
110 google::protobuf::util::JsonParseOptions options;
111 options.ignore_unknown_fields = true;
112
113 if( !google::protobuf::util::JsonStringToMessage( out.ToStdString(), &info, options ).ok() )
114 {
115 if( aReporter )
116 {
118 error.SetTitle( wxString::Format( _( "Plugin action '%s'" ), aWizard->Identifier() ) );
119 error.SetDescription( _( "could not parse wizard info" ) );
120 aReporter->Report( error );
121 }
122
123 return false;
124 }
125
126 aWizard->Info().FromProto( info );
127 return true;
128}
129
130
131tl::expected<FOOTPRINT*, wxString> FOOTPRINT_WIZARD_MANAGER::Generate( FOOTPRINT_WIZARD* aWizard )
132{
133 wxCHECK( aWizard, tl::unexpected( _( "Unexpected error with footprint wizard" ) ) );
135
136 const wxLanguageInfo* lang = wxLocale::GetLanguageInfo( Pgm().GetSelectedLanguageIdentifier() );
137
138 std::vector<wxString> args = {
139 wxS( "--generate" ),
140 wxS( "--lang" ),
141 lang->CanonicalName,
142 wxS( "--params" )
143 };
144
145 kiapi::common::types::WizardParameterList params;
146
147 for( const std::unique_ptr<WIZARD_PARAMETER>& param : aWizard->Info().parameters )
148 params.mutable_parameters()->Add( param->Pack() );
149
150 std::string paramsJson;
151
152 if( !google::protobuf::util::MessageToJsonString( params, &paramsJson ).ok() )
153 return tl::unexpected( _( "Unexpected error with footprint wizard" ) );
154
155 args.emplace_back( wxString::FromUTF8( paramsJson ) );
156
157 wxString out, err;
158 int ret = manager.InvokeActionSync( aWizard->Identifier(), args, &out, &err );
159
160 if( ret != 0 )
161 {
162 wxString error = wxString::Format( _( "Could not launch footprint wizard '%s'" ), aWizard->Info().meta.name );
163
164 err.Trim();
165 err.Trim( false );
166
167 if( !err.IsEmpty() )
168 error << wxS( "\n\n" ) << err;
169
170 return tl::unexpected( error );
171 }
172
173 kiapi::common::types::WizardGeneratedContent response;
174
175 google::protobuf::util::JsonParseOptions options;
176 options.ignore_unknown_fields = true;
177
178 if( !google::protobuf::util::JsonStringToMessage( out.ToStdString(), &response, options ).ok() )
179 {
180 wxLogTrace( traceApi, wxString::Format( "Could not decode response:\n%s", out ) );
181 return tl::unexpected( _( "Unexpected response from footprint wizard" ) );
182 }
183
184 if( response.status() != kiapi::common::types::WGS_OK )
185 return tl::unexpected( wxString::Format( _( "Footprint wizard error: %s" ), response.error_message() ) );
186
187 std::unique_ptr<FOOTPRINT> fp = std::make_unique<FOOTPRINT>( nullptr );
188
189 if( !fp->Deserialize( response.content() ) )
190 return tl::unexpected( _( "Unexpected response from footprint wizard" ) );
191
192 return fp.release();
193}
194
195
196void WIZARD_META_INFO::FromProto( const kiapi::common::types::WizardMetaInfo& aProto )
197{
198 identifier = wxString::FromUTF8( aProto.identifier() );
199 name = wxString::FromUTF8( aProto.name() );
200 description = wxString::FromUTF8( aProto.description() );
201
202 types_generated.clear();
203
204 for( int type : aProto.types_generated() )
205 types_generated.insert( static_cast<kiapi::common::types::WizardContentType>( type ) );
206}
207
208
209wxString WIZARD_PARAMETER::ParameterCategoryName( kiapi::common::types::WizardParameterCategory aCategory )
210{
211 using namespace kiapi::common::types;
212
213 switch( aCategory )
214 {
215 case WPC_PACKAGE: return _( "Package" );
216 case WPC_PADS: return _( "Pads" );
217 case WPC_3DMODEL: return _( "3D Model" );
218 case WPC_METADATA: return _( "General" );
219 case WPC_RULES: return _( "Design Rules" );
220 case WPC_UNKNOWN:
221 default:
222 wxCHECK_MSG( false, wxEmptyString, "Unhandled parameter category type!" );
223 }
224}
225
226
227std::unique_ptr<WIZARD_PARAMETER> WIZARD_PARAMETER::Create( const kiapi::common::types::WizardParameter& aProto )
228{
229 std::unique_ptr<WIZARD_PARAMETER> p;
230
231 if( aProto.has_int_() )
232 {
233 p = std::make_unique<WIZARD_INT_PARAMETER>();
234 static_cast<WIZARD_INT_PARAMETER*>( p.get() )->FromProto( aProto.int_() );
235 }
236 else if( aProto.has_real() )
237 {
238 p = std::make_unique<WIZARD_REAL_PARAMETER>();
239 static_cast<WIZARD_REAL_PARAMETER*>( p.get() )->FromProto( aProto.real() );
240 }
241 else if( aProto.has_string() )
242 {
243 p = std::make_unique<WIZARD_STRING_PARAMETER>();
244 static_cast<WIZARD_STRING_PARAMETER*>( p.get() )->FromProto( aProto.string() );
245 }
246 else if( aProto.has_bool_() )
247 {
248 p = std::make_unique<WIZARD_BOOL_PARAMETER>();
249 static_cast<WIZARD_BOOL_PARAMETER*>( p.get() )->FromProto( aProto.bool_() );
250 }
251 else if( aProto.has_enum_() )
252 {
253 p = std::make_unique<WIZARD_ENUM_PARAMETER>();
254 static_cast<WIZARD_ENUM_PARAMETER*>( p.get() )->FromProto( aProto.enum_() );
255 }
256
257 if( !p )
258 return p;
259
260 p->identifier = wxString::FromUTF8( aProto.identifier() );
261 p->name = wxString::FromUTF8( aProto.name() );
262 p->description = wxString::FromUTF8( aProto.description() );
263 p->category = aProto.category();
264 p->type = aProto.type();
265
266 return p;
267}
268
269
270kiapi::common::types::WizardParameter WIZARD_PARAMETER::Pack( bool aCompact )
271{
272 kiapi::common::types::WizardParameter packed;
273
274 packed.set_identifier( identifier.ToUTF8() );
275
276 if( !aCompact )
277 {
278 packed.set_name( name.ToUTF8() );
279 packed.set_description( description.ToUTF8() );
280 packed.set_category( category );
281 packed.set_type( type );
282 }
283
284 return packed;
285}
286
287
288kiapi::common::types::WizardParameter WIZARD_INT_PARAMETER::Pack( bool aCompact )
289{
290 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
291
292 packed.mutable_int_()->set_value( value );
293
294 if( !aCompact )
295 {
296 packed.mutable_int_()->set_default_( default_value );
297
298 if( min )
299 packed.mutable_int_()->set_min( *min );
300
301 if( max )
302 packed.mutable_int_()->set_max( *max );
303
304 if( multiple )
305 packed.mutable_int_()->set_multiple( *multiple );
306 }
307
308 return packed;
309}
310
311
312kiapi::common::types::WizardParameter WIZARD_REAL_PARAMETER::Pack( bool aCompact )
313{
314 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
315
316 packed.mutable_real()->set_value( value );
317
318 if( !aCompact )
319 {
320 packed.mutable_real()->set_default_( default_value );
321
322 if( min )
323 packed.mutable_real()->set_min( *min );
324
325 if( max )
326 packed.mutable_real()->set_max( *max );
327 }
328
329 return packed;
330}
331
332
333kiapi::common::types::WizardParameter WIZARD_BOOL_PARAMETER::Pack( bool aCompact )
334{
335 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
336
337 packed.mutable_bool_()->set_value( value );
338
339 if( !aCompact )
340 packed.mutable_bool_()->set_default_( default_value );
341
342 return packed;
343}
344
345
346kiapi::common::types::WizardParameter WIZARD_STRING_PARAMETER::Pack( bool aCompact )
347{
348 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
349
350 packed.mutable_string()->set_value( value );
351
352 if( !aCompact )
353 packed.mutable_string()->set_default_( default_value );
354
355 return packed;
356}
357
358
359kiapi::common::types::WizardParameter WIZARD_ENUM_PARAMETER::Pack( bool aCompact )
360{
361 kiapi::common::types::WizardParameter packed = WIZARD_PARAMETER::Pack();
362
363 packed.mutable_enum_()->set_value( value );
364
365 if( !aCompact )
366 {
367 packed.mutable_enum_()->set_default_( default_value );
368
369 for( const auto& [id, label] : choices )
370 {
371 kiapi::common::types::WizardEnumChoice* choice = packed.mutable_enum_()->add_choices();
372 choice->set_identifier( id.ToUTF8() );
373 choice->set_label( label.ToUTF8() );
374 }
375 }
376
377 return packed;
378}
379
380
381void WIZARD_INT_PARAMETER::FromProto( const kiapi::common::types::WizardIntParameter& aProto )
382{
383 value = aProto.value();
384 default_value = aProto.default_();
385
386 if( aProto.has_min() )
387 min = aProto.min();
388 else
389 min.reset();
390
391 if( aProto.has_max() )
392 max = aProto.max();
393 else
394 max.reset();
395
396 if( aProto.has_multiple() )
397 multiple = aProto.multiple();
398 else
399 multiple.reset();
400}
401
402
403void WIZARD_REAL_PARAMETER::FromProto( const kiapi::common::types::WizardRealParameter& aProto )
404{
405 value = aProto.value();
406 default_value = aProto.default_();
407
408 if( aProto.has_min() )
409 min = aProto.min();
410 else
411 min.reset();
412
413 if( aProto.has_max() )
414 max = aProto.max();
415 else
416 max.reset();
417}
418
419
420void WIZARD_BOOL_PARAMETER::FromProto( const kiapi::common::types::WizardBoolParameter& aProto )
421{
422 value = aProto.value();
423 default_value = aProto.default_();
424}
425
426
427void WIZARD_STRING_PARAMETER::FromProto( const kiapi::common::types::WizardStringParameter& aProto )
428{
429 value = wxString::FromUTF8( aProto.value() );
430 default_value = wxString::FromUTF8( aProto.default_() );
431
432 if( aProto.has_validation_regex() )
433 validation_regex = wxString::FromUTF8( aProto.validation_regex() );
434 else
435 validation_regex.reset();
436}
437
438
439void WIZARD_ENUM_PARAMETER::FromProto( const kiapi::common::types::WizardEnumParameter& aProto )
440{
441 value = aProto.value();
442 default_value = aProto.default_();
443
444 choices.clear();
445 choices.reserve( aProto.choices_size() );
446
447 for( const kiapi::common::types::WizardEnumChoice& choice : aProto.choices() )
448 choices.emplace_back( wxString::FromUTF8( choice.identifier() ), wxString::FromUTF8( choice.label() ) );
449}
450
451
452void WIZARD_INFO::FromProto( const kiapi::common::types::WizardInfo& aProto )
453{
454 meta.FromProto( aProto.meta() );
455
456 parameters.clear();
457 parameters.reserve( aProto.parameters_size() );
458
459 for( const kiapi::common::types::WizardParameter& parameter : aProto.parameters() )
460 {
461 if( std::unique_ptr<WIZARD_PARAMETER> param = WIZARD_PARAMETER::Create( parameter ); param )
462 parameters.emplace_back( std::move( param ) );
463 }
464}
Responsible for loading plugin definitions for API-based plugins (ones that do not run inside KiCad i...
int InvokeActionSync(const wxString &aIdentifier, std::vector< wxString > aExtraArgs, wxString *aStdout=nullptr, wxString *aStderr=nullptr, std::shared_ptr< REPORTER > aReporter=nullptr)
Invokes an action synchronously, capturing its output.
std::vector< const PLUGIN_ACTION * > GetActionsForScope(PLUGIN_ACTION_SCOPE aScope)
std::map< wxString, std::unique_ptr< FOOTPRINT_WIZARD > > m_wizards
std::vector< FOOTPRINT_WIZARD * > Wizards() const
void ReloadWizards(std::shared_ptr< REPORTER > aReporter=nullptr)
Goes through the list of IPC API plugins that provide wizard actions and attempts to refresh the info...
std::optional< FOOTPRINT_WIZARD * > GetWizard(const wxString &aIdentifier)
static bool RefreshInfo(FOOTPRINT_WIZARD *aWizard, std::shared_ptr< REPORTER > aReporter=nullptr)
Runs a wizard plugin with the –get-info argument, which should result in the plugin dumping a WizardI...
tl::expected< FOOTPRINT *, wxString > Generate(FOOTPRINT_WIZARD *aWizard)
Generates a footprint using a given wizard.
const wxString & Identifier() const
WIZARD_INFO & Info()
Holds a structured error message.
Definition ki_error.h:38
KI_ERROR & SetDescription(const wxString &aDescription)
Definition ki_error.h:64
KI_ERROR & SetTitle(const wxString &aTitle)
Definition ki_error.h:58
virtual API_PLUGIN_MANAGER & GetPluginManager() const
Definition pgm_base.h:139
void FromProto(const kiapi::common::types::WizardBoolParameter &aProto)
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
std::vector< std::pair< wxString, wxString > > choices
pair<identifier, label>
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
void FromProto(const kiapi::common::types::WizardEnumParameter &aProto)
void FromProto(const kiapi::common::types::WizardIntParameter &aProto)
std::optional< int > min
std::optional< int > multiple
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
std::optional< int > max
virtual kiapi::common::types::WizardParameter Pack(bool aCompact=true)
Packs the current state of this parameter back into a protobuf message.
kiapi::common::types::WizardParameterDataType type
static wxString ParameterCategoryName(kiapi::common::types::WizardParameterCategory aCategory)
static std::unique_ptr< WIZARD_PARAMETER > Create(const kiapi::common::types::WizardParameter &aProto)
kiapi::common::types::WizardParameterCategory category
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
void FromProto(const kiapi::common::types::WizardRealParameter &aProto)
std::optional< double > min
std::optional< double > max
std::optional< wxString > validation_regex
kiapi::common::types::WizardParameter Pack(bool aCompact=true) override
Packs the current state of this parameter back into a protobuf message.
void FromProto(const kiapi::common::types::WizardStringParameter &aProto)
#define _(s)
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
Definition api_utils.cpp:33
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
@ RPT_SEVERITY_ERROR
An action performed by a plugin via the IPC API.
Definition api_plugin.h:74
std::vector< std::unique_ptr< WIZARD_PARAMETER > > parameters
WIZARD_META_INFO meta
void FromProto(const kiapi::common::types::WizardInfo &aProto)
void FromProto(const kiapi::common::types::WizardMetaInfo &aProto)
std::set< kiapi::common::types::WizardContentType > types_generated