From 5410510bd45485fb93e435c8fa7a03465052a45a Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Wed, 5 May 2021 00:35:04 +0200 Subject: [PATCH] Spice sim: remember last simulation, remove welcome page Fixes https://gitlab.com/kicad/code/kicad/issues/8019 --- eeschema/CMakeLists.txt | 1 + eeschema/sim/sim_plot_frame.cpp | 233 +++++++++++++++------------ eeschema/sim/sim_plot_frame.h | 78 ++------- eeschema/sim/sim_plot_frame_base.cpp | 5 + eeschema/sim/sim_plot_frame_base.fbp | 22 ++- eeschema/sim/sim_plot_frame_base.h | 26 +-- eeschema/sim/sim_workbook.cpp | 100 ++++++++++++ eeschema/sim/sim_workbook.h | 151 +++++++++++++++++ eeschema/sim/spice_settings.cpp | 10 +- eeschema/sim/spice_settings.h | 6 + 10 files changed, 446 insertions(+), 186 deletions(-) create mode 100644 eeschema/sim/sim_workbook.cpp create mode 100644 eeschema/sim/sim_workbook.h diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 340cc9e8d0..9a271d4bdf 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -292,6 +292,7 @@ if( KICAD_SPICE ) sim/sim_plot_frame_base.cpp sim/sim_plot_panel.cpp sim/sim_panel_base.cpp + sim/sim_workbook.cpp sim/spice_simulator.cpp sim/spice_value.cpp dialogs/dialog_signal_list.cpp diff --git a/eeschema/sim/sim_plot_frame.cpp b/eeschema/sim/sim_plot_frame.cpp index dd6cc22fba..968dded62a 100644 --- a/eeschema/sim/sim_plot_frame.cpp +++ b/eeschema/sim/sim_plot_frame.cpp @@ -107,22 +107,6 @@ private: }; -TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName, - SIM_PLOT_TYPE aType, const wxString& aParam ) : - m_name( aName ), - m_type( aType ), - m_param( aParam ) -{ - // Title generation - m_title = wxString::Format( "%s(%s)", aParam, aName ); - - if( aType & SPT_AC_MAG ) - m_title += " (mag)"; - else if( aType & SPT_AC_PHASE ) - m_title += " (phase)"; -} - - // Store the path of saved workbooks during the session wxString SIM_PLOT_FRAME::m_savedWorkbooksPath; @@ -130,7 +114,6 @@ wxString SIM_PLOT_FRAME::m_savedWorkbooksPath; SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : SIM_PLOT_FRAME_BASE( aParent ), m_lastSimPlot( nullptr ), - m_welcomePanel( nullptr ), m_plotNumber( 0 ) { SetKiway( this, aKiway ); @@ -166,9 +149,7 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_simulator->Init(); if( m_savedWorkbooksPath.IsEmpty() ) - { m_savedWorkbooksPath = Prj().GetProjectPath(); - } m_reporter = new SIM_THREAD_REPORTER( this ); m_simulator->SetReporter( m_reporter ); @@ -228,9 +209,6 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : m_plotNotebook->SetArtProvider( new wxAuiSimpleTabArt() ); #endif - m_welcomePanel = new SIM_PANEL_BASE( wxEmptyString, m_plotNotebook, wxID_ANY ); - m_plotNotebook->AddPage( m_welcomePanel, _( "Welcome!" ), 1, true ); - // Ensure new items are taken in account by sizers: Layout(); @@ -244,6 +222,8 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : // Ensure the window is on top Raise(); + + initWorkbook(); } @@ -258,6 +238,32 @@ SIM_PLOT_FRAME::~SIM_PLOT_FRAME() } +void SIM_PLOT_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg ) +{ + EESCHEMA_SETTINGS* cfg = dynamic_cast( aCfg ); + wxASSERT( cfg ); + + if( cfg ) + { + EDA_BASE_FRAME::LoadSettings( cfg ); + + // Read subwindows sizes (should be > 0 ) + m_splitterLeftRightSashPosition = cfg->m_Simulator.plot_panel_width; + m_splitterPlotAndConsoleSashPosition = cfg->m_Simulator.plot_panel_height; + m_splitterSignalsSashPosition = cfg->m_Simulator.signal_panel_height; + m_splitterTuneValuesSashPosition = cfg->m_Simulator.cursors_panel_height; + m_plotUseWhiteBg = cfg->m_Simulator.white_background; + } + + PROJECT_FILE& project = Prj().GetProjectFile(); + + NGSPICE* currentSim = dynamic_cast( m_simulator.get() ); + + if( currentSim ) + m_simulator->Settings() = project.m_SchematicSettings->m_NgspiceSimulatorSettings; +} + + void SIM_PLOT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg ) { EESCHEMA_SETTINGS* cfg = dynamic_cast( aCfg ); @@ -287,32 +293,6 @@ void SIM_PLOT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg ) } -void SIM_PLOT_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg ) -{ - EESCHEMA_SETTINGS* cfg = dynamic_cast( aCfg ); - wxASSERT( cfg ); - - if( cfg ) - { - EDA_BASE_FRAME::LoadSettings( cfg ); - - // Read subwindows sizes (should be > 0 ) - m_splitterLeftRightSashPosition = cfg->m_Simulator.plot_panel_width; - m_splitterPlotAndConsoleSashPosition = cfg->m_Simulator.plot_panel_height; - m_splitterSignalsSashPosition = cfg->m_Simulator.signal_panel_height; - m_splitterTuneValuesSashPosition = cfg->m_Simulator.cursors_panel_height; - m_plotUseWhiteBg = cfg->m_Simulator.white_background; - } - - PROJECT_FILE& project = Prj().GetProjectFile(); - - NGSPICE* currentSim = dynamic_cast( m_simulator.get() ); - - if( currentSim ) - m_simulator->Settings() = project.m_SchematicSettings->m_NgspiceSimulatorSettings; -} - - WINDOW_SETTINGS* SIM_PLOT_FRAME::GetWindowSettings( APP_SETTINGS_BASE* aCfg ) { EESCHEMA_SETTINGS* cfg = dynamic_cast( aCfg ); @@ -322,6 +302,25 @@ WINDOW_SETTINGS* SIM_PLOT_FRAME::GetWindowSettings( APP_SETTINGS_BASE* aCfg ) } +void SIM_PLOT_FRAME::initWorkbook() +{ + m_workbook = std::make_unique(); + + if( m_simulator->Settings()->GetWorkbookPath().IsEmpty() ) + { + m_simulator->Settings()->SetWorkbookPath( Prj().GetProjectName() + ".wbk" ); + } + else + { + wxFileName filename = m_simulator->Settings()->GetWorkbookPath(); + filename.SetPath( Prj().GetProjectPath() ); + + if( !loadWorkbook( filename.GetFullPath() ) ) + DisplayErrorMessage( this, _( "There was an error while opening the workbook file" ) ); + } +} + + // A small helper struct to handle bitmaps initialisation in menus struct BM_MENU_INIT_ITEM { @@ -426,8 +425,8 @@ void SIM_PLOT_FRAME::StartSimulation( const wxString& aSimCommand ) { SIM_PANEL_BASE* plotPanel = currentPlotWindow(); - if( plotPanel && m_plots.count( plotPanel ) != 0 ) - m_exporter->SetSimCommand( m_plots[plotPanel].m_simCommand ); + if( plotPanel && m_workbook->HasPlotPanel( plotPanel ) ) + m_exporter->SetSimCommand( m_workbook->GetSimCommand( plotPanel ) ); } else { @@ -436,7 +435,7 @@ void SIM_PLOT_FRAME::StartSimulation( const wxString& aSimCommand ) if( !m_exporter->Format( &formatter, m_settingsDlg->GetNetlistOptions() ) ) { - DisplayError( this, _( "There were errors during netlist export, aborted." ) ); + DisplayErrorMessage( this, _( "There were errors during netlist export, aborted." ) ); return; } @@ -487,17 +486,11 @@ SIM_PANEL_BASE* SIM_PLOT_FRAME::NewPlotPanel( wxString aSimCommand ) plotPanel = dynamic_cast( panel ); } - if( m_welcomePanel ) - { - m_plotNotebook->DeletePage( 0 ); - m_welcomePanel = nullptr; - } - wxString pageTitle( m_simulator->TypeToName( simType, true ) ); pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), (unsigned int) ++m_plotNumber ) ); + m_workbook->AddPlotPanel( plotPanel ); m_plotNotebook->AddPage( dynamic_cast( plotPanel ), pageTitle, true ); - m_plots[plotPanel] = PLOT_INFO(); return plotPanel; } @@ -519,7 +512,7 @@ void SIM_PLOT_FRAME::AddTuner( SCH_COMPONENT* aComponent ) { SIM_PANEL_BASE* plotPanel = currentPlotWindow(); - if( !plotPanel || plotPanel == m_welcomePanel ) + if( !plotPanel ) return; // For now limit the tuner tool to RLC components @@ -551,7 +544,7 @@ void SIM_PLOT_FRAME::AddTuner( SCH_COMPONENT* aComponent ) catch( const KI_PARAM_ERROR& e ) { // Sorry, no bonus - DisplayError( nullptr, e.What() ); + DisplayErrorMessage( nullptr, e.What() ); } } @@ -656,12 +649,7 @@ void SIM_PLOT_FRAME::removePlot( const wxString& aPlotName, bool aErase ) return; if( aErase ) - { - auto& traceMap = m_plots[plotPanel].m_traces; - auto traceIt = traceMap.find( aPlotName ); - wxASSERT( traceIt != traceMap.end() ); - traceMap.erase( traceIt ); - } + m_workbook->RemoveTrace( plotPanel, aPlotName ); wxASSERT( plotPanel->TraceShown( aPlotName ) ); plotPanel->DeleteTrace( aPlotName ); @@ -772,9 +760,7 @@ bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* if( aPanel->AddTrace( name, inner, sub_x.data(), sub_y.data(), aDescriptor.GetType() ) ) - { - m_plots[aPanel].m_traces.insert( std::make_pair( name, aDescriptor ) ); - } + m_workbook->AddTrace( aPanel, name, aDescriptor ); v = v + source2.m_vincrement; offset += inner; @@ -786,9 +772,7 @@ bool SIM_PLOT_FRAME::updatePlot( const TRACE_DESC& aDescriptor, SIM_PLOT_PANEL* if( aPanel->AddTrace( aDescriptor.GetTitle(), size, data_x.data(), data_y.data(), aDescriptor.GetType() ) ) - { - m_plots[aPanel].m_traces.insert( std::make_pair( aDescriptor.GetTitle(), aDescriptor ) ); - } + m_workbook->AddTrace( aPanel, aDescriptor.GetTitle(), aDescriptor ); return true; } @@ -852,7 +836,7 @@ void SIM_PLOT_FRAME::updateSignalList() // calculated from the trace name index int imgidx = 0; - for( const auto& trace : m_plots[plotPanel].m_traces ) + for( const auto& trace : m_workbook->GetTraces( plotPanel ) ) { m_signals->InsertItem( imgidx, trace.first, imgidx ); imgidx++; @@ -901,7 +885,7 @@ void SIM_PLOT_FRAME::applyTuners() bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath ) { - m_plots.clear(); + m_workbook->Clear(); m_plotNotebook->DeleteAllPages(); wxTextFile file( aPath ); @@ -912,7 +896,10 @@ bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath ) long plotsCount; if( !file.GetFirstLine().ToLong( &plotsCount ) ) // GetFirstLine instead of GetNextLine + { + file.Close(); return false; + } for( long i = 0; i < plotsCount; ++i ) { @@ -923,8 +910,10 @@ bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath ) wxString simCommand = file.GetNextLine(); SIM_PANEL_BASE* plotPanel = NewPlotPanel( simCommand ); - m_plots[plotPanel].m_simCommand = simCommand; - StartSimulation( m_plots[plotPanel].m_simCommand ); + m_workbook->SetSimCommand( plotPanel, simCommand ); + StartSimulation( m_workbook->GetSimCommand( plotPanel ) ); + //m_plots[plotPanel].m_simCommand = simCommand; + //StartSimulation( m_plots[plotPanel].m_simCommand ); // Perform simulation, so plots can be added with values do @@ -942,18 +931,25 @@ bool SIM_PLOT_FRAME::loadWorkbook( const wxString& aPath ) wxString name, param; if( !file.GetNextLine().ToLong( &traceType ) ) + { + file.Close(); return false; + } name = file.GetNextLine(); param = file.GetNextLine(); if( name.IsEmpty() || param.IsEmpty() ) + { + file.Close(); return false; + } addPlot( name, (SIM_PLOT_TYPE) traceType, param ); } } + file.Close(); return true; } @@ -979,9 +975,19 @@ bool SIM_PLOT_FRAME::saveWorkbook( const wxString& aPath ) file.Create(); } - file.AddLine( wxString::Format( "%llu", m_plots.size() ) ); + unsigned long plotCount = 0; - for( const std::pair plot : m_plots ) + // XXX: Count all valid plots. Replace this once we move the workbook format to JSON or + // S-expressions. + for( const auto& plot : m_workbook->GetPlots() ) + { + if( plot.first ) + plotCount++; + } + + file.AddLine( wxString::Format( "%llu", plotCount ) ); + + for( const auto& plot : m_workbook->GetPlots() ) { if( plot.first ) { @@ -1001,6 +1007,10 @@ bool SIM_PLOT_FRAME::saveWorkbook( const wxString& aPath ) bool res = file.Write(); file.Close(); + // Store the path of the last saved workbook. It will be used to restore the simulation if + // the frame is closed and then opened again. + m_simulator->Settings()->SetWorkbookPath( savePath ); + return res; } @@ -1038,7 +1048,8 @@ void SIM_PLOT_FRAME::menuNewPlot( wxCommandEvent& aEvent ) // If the previous plot had the same type, copy the simulation command if( prevPlot ) - m_plots[newPlot].m_simCommand = m_plots[prevPlot].m_simCommand; + m_workbook->SetSimCommand( newPlot, m_workbook->GetSimCommand( prevPlot ) ); + //m_plots[newPlot].m_simCommand = m_plots[prevPlot].m_simCommand; } } @@ -1054,7 +1065,7 @@ void SIM_PLOT_FRAME::menuOpenWorkbook( wxCommandEvent& event ) m_savedWorkbooksPath = openDlg.GetDirectory(); if( !loadWorkbook( openDlg.GetPath() ) ) - DisplayError( this, _( "There was an error while opening the workbook file" ) ); + DisplayErrorMessage( this, _( "There was an error while opening the workbook file" ) ); } @@ -1063,16 +1074,26 @@ void SIM_PLOT_FRAME::menuSaveWorkbook( wxCommandEvent& event ) if( !CurrentPlot() ) return; - wxFileDialog saveDlg( this, _( "Save Simulation Workbook" ), m_savedWorkbooksPath, "", - WorkbookFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); + if ( !saveWorkbook( m_simulator->Settings()->GetWorkbookPath() ) ) + DisplayErrorMessage( this, _( "There was an error while saving the workbook file" ) ); +} - if( saveDlg.ShowModal() == wxID_CANCEL ) + +void SIM_PLOT_FRAME::menuSaveWorkbookAs( wxCommandEvent& event ) +{ + if( !CurrentPlot() ) return; - m_savedWorkbooksPath = saveDlg.GetDirectory(); + wxFileDialog saveAsDlg( this, _( "Save Simulation Workbook As" ), m_savedWorkbooksPath, "", + WorkbookFileWildcard(), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); - if( !saveWorkbook( saveDlg.GetPath() ) ) - DisplayError( this, _( "There was an error while saving the workbook file" ) ); + if( saveAsDlg.ShowModal() == wxID_CANCEL ) + return; + + m_savedWorkbooksPath = saveAsDlg.GetDirectory(); + + if( !saveWorkbook( saveAsDlg.GetPath() ) ) + DisplayErrorMessage( this, _( "There was an error while saving the workbook file" ) ); } @@ -1217,9 +1238,6 @@ void SIM_PLOT_FRAME::menuWhiteBackground( wxCommandEvent& event ) { wxWindow* curPage = m_plotNotebook->GetPage( page ); - if( curPage == m_welcomePanel ) - continue; - // ensure it is truly a plot panel and not the (zero plots) placeholder // which is only SIM_PLOT_PANEL_BASE SIM_PLOT_PANEL* panel = dynamic_cast( curPage ); @@ -1242,7 +1260,7 @@ void SIM_PLOT_FRAME::onPlotClose( wxAuiNotebookEvent& event ) SIM_PANEL_BASE* plotPanel = dynamic_cast( m_plotNotebook->GetPage( idx ) ); - m_plots.erase( plotPanel ); + m_workbook->RemovePlotPanel( plotPanel ); updateSignalList(); wxCommandEvent dummy; onCursorUpdate( dummy ); @@ -1305,15 +1323,23 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event ) updateNetlistExporter(); if( !m_exporter->ProcessNetlist( NET_ALL_FLAGS ) ) + { + DisplayErrorMessage( this, _( "There were errors during netlist export, aborted." ) ); return; + } - if( plotPanelWindow != m_welcomePanel ) - m_settingsDlg->SetSimCommand( m_plots[plotPanelWindow].m_simCommand ); + if( m_workbook->HasPlotPanel( plotPanelWindow ) ) + m_settingsDlg->SetSimCommand( m_workbook->GetSimCommand( plotPanelWindow ) ); if( m_settingsDlg->ShowModal() == wxID_OK ) { - wxString oldCommand = plotPanelWindow != m_welcomePanel ? - m_plots[plotPanelWindow].m_simCommand : wxString(); + wxString oldCommand; + + if( m_workbook->HasPlotPanel( plotPanelWindow ) ) + oldCommand = m_workbook->GetSimCommand( plotPanelWindow ); + else + oldCommand = wxString(); + wxString newCommand = m_settingsDlg->GetSimCommand(); SIM_TYPE newSimType = NETLIST_EXPORTER_PSPICE_SIM::CommandToSimType( newCommand ); @@ -1328,7 +1354,7 @@ void SIM_PLOT_FRAME::onSettings( wxCommandEvent& event ) plotPanelWindow = NewPlotPanel( newCommand ); } - m_plots[plotPanelWindow].m_simCommand = newCommand; + m_workbook->SetSimCommand( plotPanelWindow, newCommand ); m_simulator->Init(); } } @@ -1441,14 +1467,19 @@ void SIM_PLOT_FRAME::onShowNetlist( wxCommandEvent& event ) void SIM_PLOT_FRAME::doCloseWindow() { - SaveSettings( config() ); - if( IsSimulationRunning() ) m_simulator->Stop(); // Cancel a running simProbe or simTune tool m_schematicFrame->GetToolManager()->RunAction( ACTIONS::cancelInteractive ); + wxFileName filename = m_simulator->Settings()->GetWorkbookPath(); + filename.SetPath( Prj().GetProjectPath() ); + + saveWorkbook( filename.GetFullPath() ); + + SaveSettings( config() ); + Destroy(); } @@ -1529,17 +1560,17 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent ) // If there are any signals plotted, update them if( SIM_PANEL_BASE::IsPlottable( simType ) ) { - TRACE_MAP& traceMap = m_plots[plotPanelWindow].m_traces; SIM_PLOT_PANEL* plotPanel = dynamic_cast( plotPanelWindow ); - wxCHECK_RET( plotPanel, "not a SIM_PLOT_PANEL" ); - for( auto it = traceMap.begin(); it != traceMap.end(); /* iteration occurs in the loop */) + + for( auto it = m_workbook->TracesBegin( plotPanel ); + it != m_workbook->TracesEnd( plotPanel ); ) { if( !updatePlot( it->second, plotPanel ) ) { removePlot( it->first, false ); - it = traceMap.erase( it ); // remove a plot that does not exist anymore + it = m_workbook->RemoveTrace( plotPanel, it ); } else { diff --git a/eeschema/sim/sim_plot_frame.h b/eeschema/sim/sim_plot_frame.h index c1a879c8f7..817dfc808b 100644 --- a/eeschema/sim/sim_plot_frame.h +++ b/eeschema/sim/sim_plot_frame.h @@ -55,62 +55,11 @@ class NETLIST_EXPORTER_PSPICE_SIM; #include "sim_plot_panel.h" #include "sim_panel_base.h" +#include "sim_workbook.h" class SIM_THREAD_REPORTER; class TUNER_SLIDER; - -///< Trace descriptor class -class TRACE_DESC -{ -public: - TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName, - SIM_PLOT_TYPE aType, const wxString& aParam ); - - ///< Modifies an existing TRACE_DESC simulation type - TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, - const TRACE_DESC& aDescription, SIM_PLOT_TYPE aNewType ) - : TRACE_DESC( aExporter, aDescription.GetName(), aNewType, aDescription.GetParam() ) - { - } - - const wxString& GetTitle() const - { - return m_title; - } - - const wxString& GetName() const - { - return m_name; - } - - const wxString& GetParam() const - { - return m_param; - } - - SIM_PLOT_TYPE GetType() const - { - return m_type; - } - -private: - // Three basic parameters - ///< Name of the measured net/device - wxString m_name; - - ///< Type of the signal - SIM_PLOT_TYPE m_type; - - ///< Name of the signal parameter - wxString m_param; - - // Generated data - ///< Title displayed in the signal list/plot legend - wxString m_title; -}; - - /** * Implementing SIM_PLOT_FRAME_BASE */ @@ -189,6 +138,12 @@ public: std::shared_ptr& GetSimulatorSettings(); private: + /** + * Load the currently active workbook stored in the project settings. If there is none, + * generate a filename for the currently active workbook and store it in the project settings. + */ + void initWorkbook(); + /** * Give icons to menuitems of the main menubar */ @@ -276,6 +231,7 @@ private: void menuNewPlot( wxCommandEvent& aEvent ) override; void menuOpenWorkbook( wxCommandEvent& event ) override; void menuSaveWorkbook( wxCommandEvent& event ) override; + void menuSaveWorkbookAs( wxCommandEvent& event ) override; void menuExit( wxCommandEvent& event ) override { @@ -339,19 +295,8 @@ private: std::shared_ptr m_simulator; SIM_THREAD_REPORTER* m_reporter; - typedef std::map TRACE_MAP; - - struct PLOT_INFO - { - ///< Map of the traces displayed on the plot - TRACE_MAP m_traces; - - ///< Spice directive used to execute the simulation - wxString m_simCommand; - }; - - ///< Map of plot panels and associated data - std::map m_plots; + ///< Stores the data that can be preserved across simulator sessions + std::unique_ptr m_workbook; ///< List of currently displayed tuners std::list m_tuners; @@ -392,9 +337,6 @@ private: ///< A string to store the path of saved workbooks during a session static wxString m_savedWorkbooksPath; - ///< Info panel - SIM_PANEL_BASE* m_welcomePanel; - // Variables for temporary storage: int m_splitterLeftRightSashPosition; int m_splitterPlotAndConsoleSashPosition; diff --git a/eeschema/sim/sim_plot_frame_base.cpp b/eeschema/sim/sim_plot_frame_base.cpp index f660f3a86c..2c2958ce58 100644 --- a/eeschema/sim/sim_plot_frame_base.cpp +++ b/eeschema/sim/sim_plot_frame_base.cpp @@ -29,6 +29,10 @@ SIM_PLOT_FRAME_BASE::SIM_PLOT_FRAME_BASE( wxWindow* parent, wxWindowID id, const m_saveWorkbook = new wxMenuItem( m_fileMenu, wxID_SAVE, wxString( _("Save Workbook") ) , wxEmptyString, wxITEM_NORMAL ); m_fileMenu->Append( m_saveWorkbook ); + wxMenuItem* m_saveWorkbookAs; + m_saveWorkbookAs = new wxMenuItem( m_fileMenu, wxID_SAVE_AS, wxString( _("Save Workbook As...") ) , wxEmptyString, wxITEM_NORMAL ); + m_fileMenu->Append( m_saveWorkbookAs ); + m_fileMenu->AppendSeparator(); wxMenuItem* m_saveImage; @@ -271,6 +275,7 @@ SIM_PLOT_FRAME_BASE::SIM_PLOT_FRAME_BASE( wxWindow* parent, wxWindowID id, const m_fileMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::menuNewPlot ), this, m_newPlot->GetId()); m_fileMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::menuOpenWorkbook ), this, m_openWorkbook->GetId()); m_fileMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::menuSaveWorkbook ), this, m_saveWorkbook->GetId()); + m_fileMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::menuSaveWorkbookAs ), this, m_saveWorkbookAs->GetId()); m_fileMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::menuSaveImage ), this, m_saveImage->GetId()); m_fileMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::menuSaveCsv ), this, m_saveCsv->GetId()); m_fileMenu->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::menuExit ), this, m_exitSim->GetId()); diff --git a/eeschema/sim/sim_plot_frame_base.fbp b/eeschema/sim/sim_plot_frame_base.fbp index f9d7f0ed85..61d6b3d786 100644 --- a/eeschema/sim/sim_plot_frame_base.fbp +++ b/eeschema/sim/sim_plot_frame_base.fbp @@ -76,7 +76,7 @@ - + File m_fileMenu protected @@ -126,6 +126,20 @@ menuSaveWorkbook + + + 0 + 1 + + wxID_SAVE_AS + wxITEM_NORMAL + Save Workbook As... + m_saveWorkbookAs + none + + + menuSaveWorkbookAs + m_separator1 none @@ -388,7 +402,7 @@ - + m_sizerMain wxVERTICAL @@ -455,11 +469,11 @@ - + 5 wxEXPAND 1 - + 1 1 1 diff --git a/eeschema/sim/sim_plot_frame_base.h b/eeschema/sim/sim_plot_frame_base.h index 5089f164a1..74141a8ef7 100644 --- a/eeschema/sim/sim_plot_frame_base.h +++ b/eeschema/sim/sim_plot_frame_base.h @@ -32,18 +32,19 @@ /////////////////////////////////////////////////////////////////////////// -#define ID_SAVE_AS_IMAGE 1000 -#define ID_SAVE_AS_CSV 1001 -#define ID_MENU_RUN_SIM 1002 -#define ID_MENU_ADD_SIGNAL 1003 -#define ID_MENU_PROBE_SIGNALS 1004 -#define ID_MENU_TUNE_SIGNALS 1005 -#define ID_MENU_SHOW_NETLIST 1006 -#define ID_MENU_SET_SIMUL 1007 -#define ID_MENU_SHOW_GRID 1008 -#define ID_MENU_SHOW_LEGEND 1009 -#define ID_MENU_DOTTED 1010 -#define ID_MENU_WHITE_BG 1011 +#define wxID_SAVE_AS 1000 +#define ID_SAVE_AS_IMAGE 1001 +#define ID_SAVE_AS_CSV 1002 +#define ID_MENU_RUN_SIM 1003 +#define ID_MENU_ADD_SIGNAL 1004 +#define ID_MENU_PROBE_SIGNALS 1005 +#define ID_MENU_TUNE_SIGNALS 1006 +#define ID_MENU_SHOW_NETLIST 1007 +#define ID_MENU_SET_SIMUL 1008 +#define ID_MENU_SHOW_GRID 1009 +#define ID_MENU_SHOW_LEGEND 1010 +#define ID_MENU_DOTTED 1011 +#define ID_MENU_WHITE_BG 1012 /////////////////////////////////////////////////////////////////////////////// /// Class SIM_PLOT_FRAME_BASE @@ -94,6 +95,7 @@ class SIM_PLOT_FRAME_BASE : public KIWAY_PLAYER virtual void menuNewPlot( wxCommandEvent& event ) { event.Skip(); } virtual void menuOpenWorkbook( wxCommandEvent& event ) { event.Skip(); } virtual void menuSaveWorkbook( wxCommandEvent& event ) { event.Skip(); } + virtual void menuSaveWorkbookAs( wxCommandEvent& event ) { event.Skip(); } virtual void menuSaveImage( wxCommandEvent& event ) { event.Skip(); } virtual void menuSaveCsv( wxCommandEvent& event ) { event.Skip(); } virtual void menuExit( wxCommandEvent& event ) { event.Skip(); } diff --git a/eeschema/sim/sim_workbook.cpp b/eeschema/sim/sim_workbook.cpp new file mode 100644 index 0000000000..c0776e4866 --- /dev/null +++ b/eeschema/sim/sim_workbook.cpp @@ -0,0 +1,100 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 Mikołaj Wielgus + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +TRACE_DESC::TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName, + SIM_PLOT_TYPE aType, const wxString& aParam ) : + m_name( aName ), + m_type( aType ), + m_param( aParam ) +{ + // Title generation + m_title = wxString::Format( "%s(%s)", aParam, aName ); + + if( aType & SPT_AC_MAG ) + m_title += " (mag)"; + else if( aType & SPT_AC_PHASE ) + m_title += " (phase)"; +} + + +SIM_WORKBOOK::SIM_WORKBOOK() : + m_dirty( false ) +{ +} + + +void SIM_WORKBOOK::Clear() +{ + m_plots.clear(); +} + + +void SIM_WORKBOOK::AddPlotPanel( SIM_PANEL_BASE* aPlotPanel ) +{ + wxASSERT( m_plots.count( aPlotPanel ) == 0 ); + m_plots[aPlotPanel] = PLOT_INFO(); + + m_dirty = true; +} + + +void SIM_WORKBOOK::RemovePlotPanel( SIM_PANEL_BASE* aPlotPanel ) +{ + wxASSERT( m_plots.count( aPlotPanel ) == 1 ); + m_plots.erase( aPlotPanel ); + + m_dirty = true; +} + + +void SIM_WORKBOOK::AddTrace( const SIM_PANEL_BASE* aPlotPanel, const wxString& aName, + const TRACE_DESC& aTrace ) +{ + // XXX: A plot is created automatically if there is none with this name yet. + m_plots[aPlotPanel].m_traces.insert( + std::make_pair( aName, aTrace ) ); + + m_dirty = true; +} + + +void SIM_WORKBOOK::RemoveTrace( const SIM_PANEL_BASE* aPlotPanel, const wxString& aName ) +{ + auto& traceMap = m_plots[aPlotPanel].m_traces; + auto traceIt = traceMap.find( aName ); + wxASSERT( traceIt != traceMap.end() ); + traceMap.erase( traceIt ); + + m_dirty = true; +} + + +SIM_WORKBOOK::TRACE_MAP::const_iterator SIM_WORKBOOK::RemoveTrace( const SIM_PANEL_BASE* aPlotPanel, + TRACE_MAP::const_iterator aIt ) +{ + m_dirty = true; + return m_plots.at( aPlotPanel ).m_traces.erase( aIt ); +} diff --git a/eeschema/sim/sim_workbook.h b/eeschema/sim/sim_workbook.h new file mode 100644 index 0000000000..61910d6e15 --- /dev/null +++ b/eeschema/sim/sim_workbook.h @@ -0,0 +1,151 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2021 Mikołaj Wielgus + * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "dialog_sim_settings.h" +#include +#include + + +///< Trace descriptor class +class TRACE_DESC +{ +public: + TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, const wxString& aName, + SIM_PLOT_TYPE aType, const wxString& aParam ); + + ///< Modifies an existing TRACE_DESC simulation type + TRACE_DESC( const NETLIST_EXPORTER_PSPICE_SIM& aExporter, + const TRACE_DESC& aDescription, SIM_PLOT_TYPE aNewType ) + : TRACE_DESC( aExporter, aDescription.GetName(), aNewType, aDescription.GetParam() ) + { + } + + const wxString& GetTitle() const + { + return m_title; + } + + const wxString& GetName() const + { + return m_name; + } + + const wxString& GetParam() const + { + return m_param; + } + + SIM_PLOT_TYPE GetType() const + { + return m_type; + } + +private: + // Three basic parameters + ///< Name of the measured net/device + wxString m_name; + + ///< Type of the signal + SIM_PLOT_TYPE m_type; + + ///< Name of the signal parameter + wxString m_param; + + // Generated data + ///< Title displayed in the signal list/plot legend + wxString m_title; +}; + + +class SIM_WORKBOOK +{ +public: + typedef std::map TRACE_MAP; + + struct PLOT_INFO + { + ///< Map of the traces displayed on the plot + TRACE_MAP m_traces; + + ///< Spice directive used to execute the simulation + wxString m_simCommand; + }; + + typedef std::map PLOT_MAP; + + SIM_WORKBOOK(); + + void Clear(); + + void AddPlotPanel( SIM_PANEL_BASE* aPlotPanel ); + void RemovePlotPanel( SIM_PANEL_BASE* aPlotPanel ); + + + bool HasPlotPanel( SIM_PANEL_BASE* aPlotPanel ) const + { + return m_plots.count( aPlotPanel ) == 1; + } + + void AddTrace( const SIM_PANEL_BASE* aPlotPanel, const wxString& aName, + const TRACE_DESC& aTrace ); + void RemoveTrace( const SIM_PANEL_BASE* aPlotPanel, const wxString& aName ); + TRACE_MAP::const_iterator RemoveTrace( const SIM_PANEL_BASE* aPlotPanel, TRACE_MAP::const_iterator aIt ); + + TRACE_MAP::const_iterator TracesBegin( const SIM_PANEL_BASE* aPlotPanel ) const + { + return m_plots.at( aPlotPanel ).m_traces.cbegin(); + } + + TRACE_MAP::const_iterator TracesEnd( const SIM_PANEL_BASE* aPlotPanel ) const + { + return m_plots.at( aPlotPanel ).m_traces.cend(); + } + + void SetSimCommand( const SIM_PANEL_BASE* aPlotPanel, const wxString& aSimCommand ) + { + m_plots.at( aPlotPanel ).m_simCommand = aSimCommand; + m_dirty = true; + } + + const wxString& GetSimCommand( const SIM_PANEL_BASE* aPlotPanel ) const + { + return m_plots.at( aPlotPanel ).m_simCommand; + } + + const PLOT_MAP GetPlots() const { return m_plots; } + + const TRACE_MAP GetTraces( const SIM_PANEL_BASE* aPlotPanel ) const + { + return m_plots.at( aPlotPanel ).m_traces; + } + + bool IsDirty() const { return m_dirty; } + +private: + ///< Dirty bit, indicates something in the workbook has changed + bool m_dirty; + + ///< Map of plot panels and associated data + std::map m_plots; +}; diff --git a/eeschema/sim/spice_settings.cpp b/eeschema/sim/spice_settings.cpp index 3108b788ec..c5dabf47fc 100644 --- a/eeschema/sim/spice_settings.cpp +++ b/eeschema/sim/spice_settings.cpp @@ -35,6 +35,13 @@ SPICE_SIMULATOR_SETTINGS::SPICE_SIMULATOR_SETTINGS( JSON_SETTINGS* aParent, const std::string& aPath ) : NESTED_SETTINGS( "simulator", spiceSettingsSchemaVersion, aParent, aPath ) { + m_params.emplace_back( new PARAM( "workbook_path", &m_workbookPath, "" ) ); +} + + +bool SPICE_SIMULATOR_SETTINGS::operator==( const SPICE_SIMULATOR_SETTINGS &aRhs ) const +{ + return m_workbookPath == aRhs.m_workbookPath; } @@ -57,5 +64,6 @@ bool NGSPICE_SIMULATOR_SETTINGS::operator==( const SPICE_SIMULATOR_SETTINGS& aRh wxCHECK( settings, false ); - return m_modelMode == settings->m_modelMode; + return ( *static_cast( this ) ) == aRhs + && m_modelMode == settings->m_modelMode; } diff --git a/eeschema/sim/spice_settings.h b/eeschema/sim/spice_settings.h index a173800217..71ada21b92 100644 --- a/eeschema/sim/spice_settings.h +++ b/eeschema/sim/spice_settings.h @@ -42,6 +42,12 @@ public: virtual bool operator==( const SPICE_SIMULATOR_SETTINGS& aRhs ) const = 0; bool operator!=( const SPICE_SIMULATOR_SETTINGS& aRhs ) const { return !( *this == aRhs ); } + + wxString GetWorkbookPath() const { return m_workbookPath; } + void SetWorkbookPath( wxString aPath ) { m_workbookPath = aPath; } + +private: + wxString m_workbookPath; }; /**