KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_export_netlist.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) 2013-2017 Jean-Pierre Charras, [email protected]
5 * Copyright (C) 2013 Wayne Stambaugh <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26/* Functions relative to the dialog creating the netlist for Pcbnew. The dialog is a notebook
27 * with 7 fixed netlist formats:
28 * Pcbnew
29 * ORCADPCB2
30 * Allegro
31 * CADSTAR
32 * Pads
33 * SPICE
34 * SPICE model
35 * and up to CUSTOMPANEL_COUNTMAX user programmable formats. These external converters are
36 * referred to as plugins, but they are really just external binaries.
37 */
38
39#include <properties/property.h>
40#include <pgm_base.h>
41#include <kiface_base.h>
42#include <string_utils.h>
43#include <gestfich.h>
45#include <sch_edit_frame.h>
48#include <invoke_sch_dialog.h>
50#include <paths.h>
52#include <kiplatform/ui.h>
53
54#include <eeschema_id.h>
55#include <wx/checkbox.h>
56#include <wx/filedlg.h>
57#include <wx/msgdlg.h>
58#include <wx/process.h>
59#include <wx/regex.h>
60#include <wx/txtstrm.h>
61#include <wx/utils.h>
62
63#include <thread>
64#include <vector>
66
67
68namespace
69{
70std::vector<wxString> SplitCommandLine( const wxString& aCommand )
71{
72 std::vector<wxString> args;
73 wxString current;
74 bool inSingle = false;
75 bool inDouble = false;
76 bool argStarted = false;
77
78 for( wxUniChar c : aCommand )
79 {
80 if( c == '"' && !inSingle )
81 {
82 inDouble = !inDouble;
83 argStarted = true;
84 continue;
85 }
86
87 if( c == '\'' && !inDouble )
88 {
89 inSingle = !inSingle;
90 argStarted = true;
91 continue;
92 }
93
94 if( ( c == ' ' || c == '\t' || c == '\n' || c == '\r' ) && !inSingle && !inDouble )
95 {
96 if( argStarted || !current.IsEmpty() )
97 {
98 args.emplace_back( current );
99 current.clear();
100 argStarted = false;
101 }
102
103 continue;
104 }
105
106 current.Append( c );
107 argStarted = true;
108 }
109
110 if( argStarted || !current.IsEmpty() )
111 args.emplace_back( current );
112
113 return args;
114}
115} // namespace
116
117
118#define CUSTOMPANEL_COUNTMAX 8 // Max number of netlist plugins
119
120/*
121 * PANEL_NETLIST_INDEX values are used as index in m_PanelNetType[]
122 */
124{
125 PANELPCBNEW = 0, /* Handle Netlist format Pcbnew */
126 PANELORCADPCB2, /* Handle Netlist format OracdPcb2 */
127 PANELALLEGRO, /* Handle Netlist format Allegro */
128 PANELCADSTAR, /* Handle Netlist format CadStar */
129 PANELPADS, /* Handle Netlist format PADS */
130 PANELSPICE, /* Handle Netlist format Spice */
131 PANELSPICEMODEL, /* Handle Netlist format Spice Model (subcircuit) */
133
134 /* First auxiliary panel (custom netlists). Subsequent ones use PANELCUSTOMBASE+1,
135 * PANELCUSTOMBASE+2, etc., up to PANELCUSTOMBASE+CUSTOMPANEL_COUNTMAX-1 */
137};
138
139
140/* wxPanels for creating the NoteBook pages for each netlist format: */
141class EXPORT_NETLIST_PAGE : public wxPanel
142{
143public:
153 EXPORT_NETLIST_PAGE( wxNotebook* aParent, const wxString& aTitle, NETLIST_TYPE_ID aIdNetType, bool aCustom );
155
159 const wxString GetPageNetFmtName() { return m_pageNetFmtName; }
160
161 bool IsCustom() const { return m_custom; }
162
163public:
165
166 // opt to reformat passive component values (e.g. 1M -> 1Meg):
167 wxCheckBox* m_CurSheetAsRoot;
168 wxCheckBox* m_SaveAllVoltages;
169 wxCheckBox* m_SaveAllCurrents;
171 wxCheckBox* m_SaveAllEvents;
174 wxTextCtrl* m_TitleStringCtrl;
175 wxBoxSizer* m_LeftBoxSizer;
176 wxBoxSizer* m_RightBoxSizer;
178 wxBoxSizer* m_LowBoxSizer;
179
180private:
183};
184
185
187{
188public:
190
191 const wxString GetGeneratorTitle() { return m_textCtrlName->GetValue(); }
192 const wxString GetGeneratorTCommandLine() { return m_textCtrlCommand->GetValue(); }
193
194 bool TransferDataFromWindow() override;
195
196private:
200 void OnBrowseGenerators( wxCommandEvent& event ) override;
201
203};
204
205
206/* Event id for notebook page buttons: */
216
217
218EXPORT_NETLIST_PAGE::EXPORT_NETLIST_PAGE( wxNotebook* aParent, const wxString& aTitle,
219 NETLIST_TYPE_ID aIdNetType, bool aCustom ) :
220 wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ),
221 m_IdNetType( aIdNetType ),
222 m_CurSheetAsRoot( nullptr ),
223 m_SaveAllVoltages( nullptr ),
224 m_SaveAllCurrents( nullptr ),
225 m_SaveAllDissipations( nullptr ),
226 m_SaveAllEvents( nullptr ),
227 m_RunExternalSpiceCommand( nullptr ),
228 m_CommandStringCtrl( nullptr ),
229 m_TitleStringCtrl( nullptr ),
230 m_pageNetFmtName( aTitle ),
231 m_custom( aCustom )
232{
233 aParent->AddPage( this, aTitle, false );
234
235 wxBoxSizer* MainBoxSizer = new wxBoxSizer( wxVERTICAL );
236 SetSizer( MainBoxSizer );
237 wxBoxSizer* UpperBoxSizer = new wxBoxSizer( wxHORIZONTAL );
238 m_LowBoxSizer = new wxBoxSizer( wxVERTICAL );
239 MainBoxSizer->Add( UpperBoxSizer, 0, wxEXPAND | wxALL, 5 );
240 MainBoxSizer->Add( m_LowBoxSizer, 0, wxEXPAND | wxALL, 5 );
241
242 m_LeftBoxSizer = new wxBoxSizer( wxVERTICAL );
243 m_RightBoxSizer = new wxBoxSizer( wxVERTICAL );
244 m_RightOptionsBoxSizer = new wxBoxSizer( wxVERTICAL );
245 UpperBoxSizer->Add( m_LeftBoxSizer, 0, wxEXPAND | wxALL, 5 );
246 UpperBoxSizer->Add( m_RightBoxSizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
247 UpperBoxSizer->Add( m_RightOptionsBoxSizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5 );
248}
249
250
252 DIALOG_EXPORT_NETLIST( aEditFrame, aEditFrame )
253{
254}
255
256
258 JOB_EXPORT_SCH_NETLIST* aJob ) :
260 m_job( aJob )
261{
262 m_editFrame = aEditFrame;
263
264 // Initialize the array of netlist pages
266
267 // Add notebook pages:
268 EXPORT_NETLIST_PAGE* page = nullptr;
269 wxStaticText* label = nullptr;
270
271 page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "KiCad" ), NET_TYPE_PCBNEW, false );
272 label = new wxStaticText( page, wxID_ANY, _( "Export netlist in KiCad format" ) );
273 page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 );
275
276 page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "OrcadPCB2" ), NET_TYPE_ORCADPCB2, false );
277 label = new wxStaticText( page, wxID_ANY, _( "Export netlist in OrcadPCB2 format" ) );
278 page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 );
280
281 page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "Allegro" ), NET_TYPE_ALLEGRO, false );
282 label = new wxStaticText( page, wxID_ANY, _( "Export netlist in Allegro format" ) );
283 page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 );
285
286 page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "CadStar" ), NET_TYPE_CADSTAR, false );
287 label = new wxStaticText( page, wxID_ANY, _( "Export netlist in CadStar format" ) );
288 page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 );
290
291 page = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "PADS" ), NET_TYPE_PADS, false );
292 label = new wxStaticText( page, wxID_ANY, _( "Export netlist in PADS format" ) );
293 page->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 );
295
298
299 if( !m_job )
300 {
301 m_outputPath->Hide();
304
305 SetupStandardButtons( { { wxID_OK, _( "Export Netlist" ) },
306 { wxID_CANCEL, _( "Close" ) } } );
307 }
308 else
309 {
310 SetTitle( m_job->GetSettingsDialogTitle() );
311
312 m_MessagesBox->Hide();
313 m_outputPath->SetValue( m_job->GetConfiguredOutputPath() );
314
316
317 // custom netlist (external invokes, not supported)
318 for( int ii = 0; ii < DEFINED_NETLISTS_COUNT + CUSTOMPANEL_COUNTMAX; ++ii )
319 {
320 if( EXPORT_NETLIST_PAGE* candidate = m_PanelNetType[ii] )
321 {
322 if( candidate->GetPageNetFmtName() == JOB_EXPORT_SCH_NETLIST::GetFormatNameMap()[m_job->format] )
323 {
324 m_NoteBook->ChangeSelection( ii );
325 break;
326 }
327 }
328 }
329
330 m_buttonAddGenerator->Hide();
331 m_buttonDelGenerator->Hide();
332 }
333
334 // DIALOG_SHIM needs a unique hash_key because classname will be the same for both job and
335 // non-job versions.
336 m_hash_key = TO_UTF8( GetTitle() );
337
338 // Now all widgets have the size fixed, call FinishDialogSettings
340
342}
343
344
346{
347 EXPORT_NETLIST_PAGE* pg = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "SPICE" ), NET_TYPE_SPICE, false );
348
349 wxStaticText* label = new wxStaticText( pg, wxID_ANY, _( "Export netlist in SPICE format" ) );
350 pg->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 );
351
352 pg->m_CurSheetAsRoot = new wxCheckBox( pg, ID_CUR_SHEET_AS_ROOT, _( "Use current sheet as root" ) );
353 pg->m_CurSheetAsRoot->SetToolTip( _( "Export netlist only for the current sheet" ) );
354 pg->m_LeftBoxSizer->Add( pg->m_CurSheetAsRoot, 0, wxGROW | wxBOTTOM | wxRIGHT, 5 );
355
356 pg->m_SaveAllVoltages = new wxCheckBox( pg, ID_SAVE_ALL_VOLTAGES, _( "Save all voltages" ) );
357 pg->m_SaveAllVoltages->SetToolTip( _( "Write a directive to save all voltages (.save all)" ) );
358 pg->m_LeftBoxSizer->Add( pg->m_SaveAllVoltages, 0, wxBOTTOM | wxRIGHT, 5 );
359
360 pg->m_SaveAllCurrents = new wxCheckBox( pg, ID_SAVE_ALL_CURRENTS, _( "Save all currents" ) );
361 pg->m_SaveAllCurrents->SetToolTip( _( "Write a directive to save all currents (.probe alli)" ) );
362 pg->m_LeftBoxSizer->Add( pg->m_SaveAllCurrents, 0, wxBOTTOM | wxRIGHT, 5 );
363
364 pg->m_SaveAllDissipations = new wxCheckBox( pg, ID_SAVE_ALL_DISSIPATIONS, _( "Save all power dissipations" ) );
365 pg->m_SaveAllDissipations->SetToolTip( _( "Write directives to save power dissipation of all items "
366 "(.probe p(<item>))" ) );
367 pg->m_LeftBoxSizer->Add( pg->m_SaveAllDissipations, 0, wxBOTTOM | wxRIGHT, 5 );
368
369 pg->m_SaveAllEvents = new wxCheckBox( pg, ID_SAVE_ALL_EVENTS, _( "Save all digital event data" ) );
370 pg->m_SaveAllEvents->SetToolTip( _( "If not set, write a directive to prevent the saving of digital event data "
371 "(esave none)" ) );
372 pg->m_LeftBoxSizer->Add( pg->m_SaveAllEvents, 0, wxBOTTOM | wxRIGHT, 5 );
373
374
375 pg->m_RunExternalSpiceCommand = new wxCheckBox( pg, ID_RUN_SIMULATOR, _( "Run external simulator command:" ) );
376 pg->m_RunExternalSpiceCommand->SetToolTip( _( "Enter the command line to run SPICE\n"
377 "Usually '<path to SPICE binary> \"%I\"'\n"
378 "%I will be replaced by the netlist filepath" ) );
379 pg->m_LowBoxSizer->Add( pg->m_RunExternalSpiceCommand, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
380
381 pg->m_CommandStringCtrl = new wxTextCtrl( pg, wxID_ANY, wxT( "spice \"%I\"" ) );
382
383 pg->m_CommandStringCtrl->SetInsertionPoint( 1 );
384 pg->m_LowBoxSizer->Add( pg->m_CommandStringCtrl, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );
385
387}
388
389
391{
392 auto* pg = new EXPORT_NETLIST_PAGE( m_NoteBook, wxT( "SPICE Model" ), NET_TYPE_SPICE_MODEL, false );
393
394 wxStaticText* label = new wxStaticText( pg, wxID_ANY, _( "Export netlist as a SPICE .subckt model" ) );
395 pg->m_LeftBoxSizer->Add( label, 0, wxBOTTOM, 10 );
396
397 pg->m_CurSheetAsRoot = new wxCheckBox( pg, ID_CUR_SHEET_AS_ROOT, _( "Use current sheet as root" ) );
398 pg->m_CurSheetAsRoot->SetToolTip( _( "Export netlist only for the current sheet" ) );
399 pg->m_LeftBoxSizer->Add( pg->m_CurSheetAsRoot, 0, wxEXPAND | wxBOTTOM | wxRIGHT, 5 );
400
402}
403
404
406{
407 EXPORT_NETLIST_PAGE* currPage;
408 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
409 wxCHECK( cfg, /* void */ );
410
411 for( size_t i = 0; i < CUSTOMPANEL_COUNTMAX && i < cfg->m_NetlistPanel.plugins.size(); i++ )
412 {
413 // pairs of (title, command) are stored
414 currPage = AddOneCustomPage( cfg->m_NetlistPanel.plugins[i].name,
415 cfg->m_NetlistPanel.plugins[i].command,
416 static_cast<NETLIST_TYPE_ID>( NET_TYPE_CUSTOM1 + i ) );
417
418 m_PanelNetType[PANELCUSTOMBASE + i] = currPage;
419 }
420}
421
422
424 const wxString& aCommandString,
425 NETLIST_TYPE_ID aNetTypeId )
426{
427 EXPORT_NETLIST_PAGE* pg = new EXPORT_NETLIST_PAGE( m_NoteBook, aTitle, aNetTypeId, true );
428
429 pg->m_LowBoxSizer->Add( new wxStaticText( pg, wxID_ANY, _( "Title:" ) ), 0,
430 wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
431
432 pg->m_LowBoxSizer->AddSpacer( 2 );
433
434 pg->m_TitleStringCtrl = new wxTextCtrl( pg, wxID_ANY, aTitle );
435
436 pg->m_TitleStringCtrl->SetInsertionPoint( 1 );
437 pg->m_LowBoxSizer->Add( pg->m_TitleStringCtrl, 0, wxEXPAND | wxLEFT | wxRIGHT, 5 );
438
439 pg->m_LowBoxSizer->AddSpacer( 10 );
440
441 pg->m_LowBoxSizer->Add( new wxStaticText( pg, wxID_ANY, _( "Netlist command:" ) ), 0,
442 wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
443
444 pg->m_LowBoxSizer->AddSpacer( 2 );
445
446 pg->m_CommandStringCtrl = new wxTextCtrl( pg, wxID_ANY, aCommandString );
447
448 pg->m_CommandStringCtrl->SetInsertionPoint( 1 );
449 pg->m_LowBoxSizer->Add( pg->m_CommandStringCtrl, 0, wxEXPAND | wxLEFT | wxRIGHT, 5 );
450
451 return pg;
452}
453
454
456{
458}
459
460
462{
463 wxFileName fn;
464 wxString fileWildcard;
465 wxString fileExt;
466 wxString title = _( "Save Netlist File" );
467
468 if( m_job )
469 {
470 for( const auto& [format, name] : JOB_EXPORT_SCH_NETLIST::GetFormatNameMap() )
471 {
472 if( name == m_PanelNetType[m_NoteBook->GetSelection()]->GetPageNetFmtName() )
473 {
474 m_job->format = format;
475 break;
476 }
477 }
478
479 m_job->SetConfiguredOutputPath( m_outputPath->GetValue() );
480 m_job->m_spiceSaveAllVoltages = m_PanelNetType[ PANELSPICE ]->m_SaveAllVoltages->IsChecked();
481 m_job->m_spiceSaveAllCurrents = m_PanelNetType[ PANELSPICE ]->m_SaveAllCurrents->IsChecked();
482 m_job->m_spiceSaveAllDissipations = m_PanelNetType[ PANELSPICE ]->m_SaveAllDissipations->IsChecked();
483 m_job->m_spiceSaveAllEvents = m_PanelNetType[ PANELSPICE ]->m_SaveAllEvents->IsChecked();
484
485 return true;
486 }
487
488 EXPORT_NETLIST_PAGE* currPage;
489 currPage = (EXPORT_NETLIST_PAGE*) m_NoteBook->GetCurrentPage();
490
491 bool runExternalSpiceCommand = false;
492 unsigned netlist_opt = 0;
493
494 // Calculate the netlist filename
495 fn = m_editFrame->Schematic().GetFileName();
496 FilenamePrms( currPage->m_IdNetType, &fileExt, &fileWildcard );
497
498 // Set some parameters
499 switch( currPage->m_IdNetType )
500 {
501 case NET_TYPE_SPICE:
502 // Set spice netlist options:
504
505 if( currPage->m_SaveAllVoltages->GetValue() )
507
508 if( currPage->m_SaveAllCurrents->GetValue() )
510
511 if( currPage->m_SaveAllDissipations->GetValue() )
513
514 if( currPage->m_SaveAllEvents->GetValue() )
516
517 if( currPage->m_CurSheetAsRoot->GetValue() )
519
520 runExternalSpiceCommand = currPage->m_RunExternalSpiceCommand->GetValue();
521 break;
522
524 if( currPage->m_CurSheetAsRoot->GetValue() )
526
527 break;
528
529 case NET_TYPE_CADSTAR:
530 break;
531
532 case NET_TYPE_PCBNEW:
533 break;
534
536 break;
537
538 case NET_TYPE_ALLEGRO:
539 break;
540
541 case NET_TYPE_PADS:
542 break;
543
544 default: // custom, NET_TYPE_CUSTOM1 and greater
545 title.Printf( _( "%s Export" ), currPage->m_TitleStringCtrl->GetValue() );
546 break;
547 }
548
549 wxString fullpath;
550
551 if( runExternalSpiceCommand )
552 {
553 fn.SetExt( FILEEXT::SpiceFileExtension );
554 fullpath = fn.GetFullPath();
555 }
556 else
557 {
558 fn.SetExt( fileExt );
559
560 if( fn.GetPath().IsEmpty() )
561 fn.SetPath( wxPathOnly( Prj().GetProjectFullName() ) );
562
563 wxString fullname = fn.GetFullName();
564 wxString path = fn.GetPath();
565
566 // full name does not and should not include the path, per wx docs.
567 wxFileDialog dlg( this, title, path, fullname, fileWildcard, wxFD_SAVE );
568
570
571 if( dlg.ShowModal() == wxID_CANCEL )
572 return false;
573
574 fullpath = dlg.GetPath(); // directory + filename
575 }
576
577 m_editFrame->ClearMsgPanel();
578 REPORTER& reporter = m_MessagesBox->Reporter();
579
580 if( currPage->m_CommandStringCtrl )
581 m_editFrame->SetNetListerCommand( currPage->m_CommandStringCtrl->GetValue() );
582 else
583 m_editFrame->SetNetListerCommand( wxEmptyString );
584
585 if( !m_editFrame->ReadyToNetlist( _( "Exporting netlist requires a fully annotated schematic." ) ) )
586 return false;
587
588 m_editFrame->WriteNetListFile( currPage->m_IdNetType, fullpath, netlist_opt, &reporter );
589
590 if( runExternalSpiceCommand )
591 {
592 // Build the command line
593 wxString commandLine = m_PanelNetType[ PANELSPICE ]->m_CommandStringCtrl->GetValue();
594 commandLine.Replace( wxS( "%I" ), fullpath, true );
595 commandLine.Trim( true ).Trim( false );
596
597 if( !commandLine.IsEmpty() )
598 {
599 std::vector<wxString> argsStrings = SplitCommandLine( commandLine );
600
601 if( !argsStrings.empty() )
602 {
603 std::vector<const wxChar*> argv;
604 argv.reserve( argsStrings.size() + 1 );
605
606 for( wxString& arg : argsStrings )
607 argv.emplace_back( arg.wc_str() );
608
609 argv.emplace_back( nullptr );
610
611 wxExecuteEnv env;
612 wxGetEnvMap( &env.env );
613
614 const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
615
616 for( const auto& [key, value] : envVars )
617 {
618 if( !value.GetValue().IsEmpty() )
619 env.env[key] = value.GetValue();
620 }
621
622 wxFileName netlistFile( fullpath );
623
624 if( !netlistFile.IsAbsolute() )
625 netlistFile.MakeAbsolute( Prj().GetProjectPath() );
626 else
627 netlistFile.MakeAbsolute();
628
629 wxString cwd = netlistFile.GetPath();
630
631 if( cwd.IsEmpty() )
632 cwd = Prj().GetProjectPath();
633
634 env.cwd = cwd;
635
636 wxProcess* process = new wxProcess( GetEventHandler(), wxID_ANY );
637 process->Redirect();
638
639 long pid = wxExecute( argv.data(), wxEXEC_ASYNC, process, &env );
640
641 reporter.ReportHead( commandLine, RPT_SEVERITY_ACTION );
642
643 if( pid <= 0 )
644 {
645 reporter.Report( _( "external simulator not found" ), RPT_SEVERITY_ERROR );
646 reporter.Report( _( "Note: command line is usually: "
647 "<tt>&lt;path to SPICE binary&gt; \"%I\"</tt>" ),
649 delete process;
650 }
651 else
652 {
653 process->Activate();
654
655 std::this_thread::sleep_for( std::chrono::seconds( 1 ) ); // give the process time to start and output any data or errors
656
657 if( process->IsInputAvailable() )
658 {
659 wxInputStream* in = process->GetInputStream();
660 wxTextInputStream textstream( *in );
661
662 while( in->CanRead() )
663 {
664 wxString line = textstream.ReadLine();
665
666 if( !line.IsEmpty() )
667 reporter.Report( line, RPT_SEVERITY_INFO );
668 }
669 }
670
671 if( process->IsErrorAvailable() )
672 {
673 wxInputStream* err = process->GetErrorStream();
674 wxTextInputStream textstream( *err );
675
676 while( err->CanRead() )
677 {
678 wxString line = textstream.ReadLine();
679
680 if( !line.IsEmpty() )
681 {
682 if( line.EndsWith( wxS( "failed with error 2!" ) ) ) // ENOENT
683 {
684 reporter.Report( _( "external simulator not found" ), RPT_SEVERITY_ERROR );
685 reporter.Report( _( "Note: command line is usually: "
686 "<tt>&lt;path to SPICE binary&gt; \"%I\"</tt>" ),
688 }
689 else if( line.EndsWith( wxS( "failed with error 8!" ) ) ) // ENOEXEC
690 {
691 reporter.Report( _( "external simulator has the wrong format or "
692 "architecture" ), RPT_SEVERITY_ERROR );
693 }
694 else if( line.EndsWith( "failed with error 13!" ) ) // EACCES
695 {
696 reporter.Report( _( "permission denied" ), RPT_SEVERITY_ERROR );
697 }
698 else
699 {
700 reporter.Report( line, RPT_SEVERITY_ERROR );
701 }
702 }
703 }
704 }
705
706 process->CloseOutput();
707 process->Detach();
708
709 // Do not delete process, it will delete itself when it terminates
710 }
711 }
712 }
713 }
714
716
717 return !runExternalSpiceCommand;
718}
719
720
721bool DIALOG_EXPORT_NETLIST::FilenamePrms( NETLIST_TYPE_ID aType, wxString * aExt, wxString * aWildCard )
722{
723 wxString fileExt;
724 wxString fileWildcard;
725 bool ret = true;
726
727 switch( aType )
728 {
729 case NET_TYPE_SPICE:
731 fileWildcard = FILEEXT::SpiceNetlistFileWildcard();
732 break;
733
734 case NET_TYPE_CADSTAR:
737 break;
738
742 break;
743
744 case NET_TYPE_PCBNEW:
746 fileWildcard = FILEEXT::NetlistFileWildcard();
747 break;
748
749 case NET_TYPE_ALLEGRO:
752 break;
753
754 case NET_TYPE_PADS:
756 fileWildcard = FILEEXT::PADSNetlistFileWildcard();
757 break;
758
759
760 default: // custom, NET_TYPE_CUSTOM1 and greater
761 fileWildcard = FILEEXT::AllFilesWildcard();
762 ret = false;
763 }
764
765 if( aExt )
766 *aExt = fileExt;
767
768 if( aWildCard )
769 *aWildCard = fileWildcard;
770
771 return ret;
772}
773
774
776{
777 EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
778 wxCHECK( cfg, /* void */ );
779
780 cfg->m_NetlistPanel.plugins.clear();
781
782 // Update existing custom pages
783 for( int ii = PANELCUSTOMBASE; ii < PANELCUSTOMBASE + CUSTOMPANEL_COUNTMAX; ++ii )
784 {
785 if( EXPORT_NETLIST_PAGE* currPage = m_PanelNetType[ii] )
786 {
787 wxString title = currPage->m_TitleStringCtrl->GetValue();
788 wxString command = currPage->m_CommandStringCtrl->GetValue();
789
790 if( title.IsEmpty() || command.IsEmpty() )
791 continue;
792
793 cfg->m_NetlistPanel.plugins.emplace_back( title, wxEmptyString );
794 cfg->m_NetlistPanel.plugins.back().command = command;
795 }
796 }
797}
798
799
800void DIALOG_EXPORT_NETLIST::OnDelGenerator( wxCommandEvent& event )
801{
802 EXPORT_NETLIST_PAGE* currPage = (EXPORT_NETLIST_PAGE*) m_NoteBook->GetCurrentPage();
803
804 if( !currPage->IsCustom() )
805 return;
806
807 currPage->m_CommandStringCtrl->SetValue( wxEmptyString );
808 currPage->m_TitleStringCtrl->SetValue( wxEmptyString );
809
811
812 if( IsQuasiModal() )
814 else
815 EndDialog( NET_PLUGIN_CHANGE );
816}
817
818
819void DIALOG_EXPORT_NETLIST::OnAddGenerator( wxCommandEvent& event )
820{
822
823 if( dlg.ShowModal() != wxID_OK )
824 return;
825
826 wxString title = dlg.GetGeneratorTitle();
827 wxString cmd = dlg.GetGeneratorTCommandLine();
828
829 // Verify it does not exists
830 for( int ii = PANELCUSTOMBASE; ii < PANELCUSTOMBASE + CUSTOMPANEL_COUNTMAX; ++ii )
831 {
832 if( m_PanelNetType[ii] && m_PanelNetType[ii]->GetPageNetFmtName() == title )
833 {
834 wxMessageBox( _( "This plugin already exists." ) );
835 return;
836 }
837 }
838
839 // Find the first empty slot
840 int netTypeId = PANELCUSTOMBASE;
841
842 while( m_PanelNetType[netTypeId] )
843 {
844 netTypeId++;
845
846 if( netTypeId == PANELCUSTOMBASE + CUSTOMPANEL_COUNTMAX )
847 {
848 wxMessageBox( _( "Maximum number of plugins already added to dialog." ) );
849 return;
850 }
851 }
852
853 m_PanelNetType[netTypeId] = AddOneCustomPage( title, cmd, (NETLIST_TYPE_ID)netTypeId );
854
856
857 if( IsQuasiModal() )
859 else
860 EndDialog( NET_PLUGIN_CHANGE );
861}
862
863
873
874
876{
877 if( !wxDialog::TransferDataFromWindow() )
878 return false;
879
880 if( m_textCtrlName->GetValue() == wxEmptyString )
881 {
882 wxMessageBox( _( "You must provide a netlist generator title" ) );
883 return false;
884 }
885
886 return true;
887}
888
889
891{
892 wxString FullFileName, Path;
893
894#ifndef __WXMAC__
895 Path = Pgm().GetExecutablePath();
896#else
897 Path = PATHS::GetOSXKicadDataDir() + wxT( "/plugins" );
898#endif
899
900 FullFileName = wxFileSelector( _( "Generator File" ), Path, FullFileName, wxEmptyString,
901 wxFileSelectorDefaultWildcardStr, wxFD_OPEN, this );
902
903 if( FullFileName.IsEmpty() )
904 return;
905
906 // Creates a default command line, suitable for external tool xslproc or python, based on
907 // the plugin extension ("xsl" or "exe" or "py")
908 wxString cmdLine;
909 wxFileName fn( FullFileName );
910 wxString ext = fn.GetExt();
911
912 if( ext == wxT( "xsl" ) )
913 cmdLine.Printf( wxT( "xsltproc -o \"%%O\" \"%s\" \"%%I\"" ), FullFileName );
914 else if( ext == wxT( "exe" ) || ext.IsEmpty() )
915 cmdLine.Printf( wxT( "\"%s\" > \"%%O\" < \"%%I\"" ), FullFileName );
916 else if( ext == wxT( "py" ) || ext.IsEmpty() )
917 cmdLine.Printf( wxT( "python \"%s\" \"%%I\" \"%%O\"" ), FullFileName );
918 else
919 cmdLine.Printf( wxT( "\"%s\"" ), FullFileName );
920
921 m_textCtrlCommand->SetValue( cmdLine );
922
923 // We need a title for this panel
924 // Propose a default value if empty ( i.e. the short filename of the script)
925 if( m_textCtrlName->GetValue().IsEmpty() )
926 m_textCtrlName->SetValue( fn.GetName() );
927}
928
929
931{
932 EXPORT_NETLIST_PAGE* currPage = (EXPORT_NETLIST_PAGE*) m_NoteBook->GetCurrentPage();
933
934 if( currPage == nullptr )
935 return;
936
937 m_buttonDelGenerator->Enable( currPage->IsCustom() );
938}
939
940
942{
943 DIALOG_EXPORT_NETLIST dlg( aCaller );
944
945 int ret = dlg.ShowModal();
946 aCaller->SaveProjectLocalSettings();
947
948 return ret;
949}
const char * name
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
DIALOG_EXPORT_NETLIST_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Export Netlist"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
void OnDelGenerator(wxCommandEvent &event) override
Remove a panel relative to a netlist plugin.
void WriteCurrentNetlistSetup()
Write the current netlist options setup in the configuration.
EXPORT_NETLIST_PAGE * AddOneCustomPage(const wxString &aTitle, const wxString &aCommandString, NETLIST_TYPE_ID aNetTypeId)
std::vector< EXPORT_NETLIST_PAGE * > m_PanelNetType
JOB_EXPORT_SCH_NETLIST * m_job
void OnAddGenerator(wxCommandEvent &event) override
Add a new panel for a new netlist plugin.
void OnNetlistTypeSelection(wxNotebookEvent &event) override
bool FilenamePrms(NETLIST_TYPE_ID aType, wxString *aExt, wxString *aWildCard)
Return the filename extension and the wildcard string for this page or a void name if there is no def...
DIALOG_EXPORT_NETLIST(SCH_EDIT_FRAME *aEditFrame)
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
bool IsQuasiModal() const
Definition dialog_shim.h:93
void EndQuasiModal(int retCode)
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
wxWindow * m_initialFocusTarget
int ShowModal() override
PANEL_NETLIST m_NetlistPanel
const wxString GetPageNetFmtName()
~EXPORT_NETLIST_PAGE()=default
EXPORT_NETLIST_PAGE(wxNotebook *aParent, const wxString &aTitle, NETLIST_TYPE_ID aIdNetType, bool aCustom)
Create a setup page for one netlist format.
static std::map< JOB_EXPORT_SCH_NETLIST::FORMAT, wxString > & GetFormatNameMap()
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:95
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
NETLIST_DIALOG_ADD_GENERATOR_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Add Script-based Netlist Exporter"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
NETLIST_DIALOG_ADD_GENERATOR(DIALOG_EXPORT_NETLIST *parent)
void OnBrowseGenerators(wxCommandEvent &event) override
Browse plugin files, and set m_CommandStringCtrl field.
virtual ENV_VAR_MAP & GetLocalEnvVariables() const
Definition pgm_base.cpp:787
virtual const wxString & GetExecutablePath() const
Definition pgm_base.cpp:867
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:177
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & ReportHead(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Places the report at the beginning of the list for objects that support ordering.
Definition reporter.h:121
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
Schematic editor (Eeschema) main window.
void SaveProjectLocalSettings() override
Save changes to the project settings to the project (.pro) file.
@ DEFINED_NETLISTS_COUNT
@ ID_SAVE_ALL_VOLTAGES
@ ID_SAVE_ALL_CURRENTS
@ ID_CUR_SHEET_AS_ROOT
@ ID_SAVE_ALL_DISSIPATIONS
#define CUSTOMPANEL_COUNTMAX
int InvokeDialogNetList(SCH_EDIT_FRAME *aCaller)
#define _(s)
NETLIST_TYPE_ID
netlist types
@ ID_END_EESCHEMA_ID_LIST
Definition eeschema_id.h:78
static const std::string CadstarNetlistFileExtension
static const std::string NetlistFileExtension
static const std::string OrCadPcb2NetlistFileExtension
static const std::string SpiceFileExtension
static const std::string PADSNetlistFileExtension
static const std::string AllegroNetlistFileExtension
static wxString SpiceNetlistFileWildcard()
static wxString OrCadPcb2NetlistFileWildcard()
static wxString AllFilesWildcard()
static wxString AllegroNetlistFileWildcard()
static wxString CadstarNetlistFileWildcard()
static wxString PADSNetlistFileWildcard()
static wxString NetlistFileWildcard()
std::map< wxString, ENV_VAR_ITEM > ENV_VAR_MAP
#define NET_PLUGIN_CHANGE
Create and shows DIALOG_EXPORT_NETLIST and returns whatever DIALOG_EXPORT_NETLIST::ShowModal() return...
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:435
static PGM_BASE * process
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
std::vector< NETLIST_PLUGIN_SETTINGS > plugins
std::string path
Definition of file extensions used in Kicad.