mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 18:23:15 +02:00
CHANGED abandon the unpredictable behaviour of the Simulation Command dialog. You now separately add simulation tabs (which have invariant command types once created), and the dialog edits the current tab. Also a bunch of bug fixes to make multiple simulation plots actually work.
789 lines
22 KiB
C++
789 lines
22 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2016-2023 CERN
|
|
* Copyright (C) 2016-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
*
|
|
* 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 3
|
|
* 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:
|
|
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
* or you may search the http://www.gnu.org website for the version 3 license,
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <wx/debug.h>
|
|
|
|
// For some obscure reason, needed on msys2 with some wxWidgets versions (3.0) to avoid
|
|
// undefined symbol at link stage (due to use of #include <pegtl.hpp>)
|
|
// Should not create issues on other platforms
|
|
#include <wx/menu.h>
|
|
|
|
#include <project/project_file.h>
|
|
#include <sch_edit_frame.h>
|
|
#include <kiway.h>
|
|
#include <confirm.h>
|
|
#include <bitmaps.h>
|
|
#include <wildcards_and_files_ext.h>
|
|
#include <widgets/tuner_slider.h>
|
|
#include <widgets/grid_color_swatch_helpers.h>
|
|
#include <widgets/wx_grid.h>
|
|
#include <tool/tool_manager.h>
|
|
#include <tool/tool_dispatcher.h>
|
|
#include <tool/action_manager.h>
|
|
#include <tool/action_toolbar.h>
|
|
#include <tool/common_control.h>
|
|
#include <tools/simulator_control.h>
|
|
#include <tools/ee_actions.h>
|
|
#include <string_utils.h>
|
|
#include <pgm_base.h>
|
|
#include "ngspice.h"
|
|
#include <sim/simulator_frame.h>
|
|
#include <sim/simulator_panel.h>
|
|
#include <sim/sim_plot_panel.h>
|
|
#include <sim/spice_simulator.h>
|
|
#include <sim/spice_reporter.h>
|
|
#include <eeschema_settings.h>
|
|
#include <advanced_config.h>
|
|
|
|
#include <memory>
|
|
|
|
|
|
class SIM_THREAD_REPORTER : public SPICE_REPORTER
|
|
{
|
|
public:
|
|
SIM_THREAD_REPORTER( SIMULATOR_FRAME* aParent ) :
|
|
m_parent( aParent )
|
|
{
|
|
}
|
|
|
|
REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
|
|
{
|
|
wxCommandEvent* event = new wxCommandEvent( EVT_SIM_REPORT );
|
|
event->SetString( aText );
|
|
wxQueueEvent( m_parent, event );
|
|
return *this;
|
|
}
|
|
|
|
bool HasMessage() const override
|
|
{
|
|
return false; // Technically "indeterminate" rather than false.
|
|
}
|
|
|
|
void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override
|
|
{
|
|
wxCommandEvent* event = nullptr;
|
|
|
|
switch( aNewState )
|
|
{
|
|
case SIM_IDLE: event = new wxCommandEvent( EVT_SIM_FINISHED ); break;
|
|
case SIM_RUNNING: event = new wxCommandEvent( EVT_SIM_STARTED ); break;
|
|
default: wxFAIL; return;
|
|
}
|
|
|
|
wxQueueEvent( m_parent, event );
|
|
}
|
|
|
|
private:
|
|
SIMULATOR_FRAME* m_parent;
|
|
};
|
|
|
|
|
|
BEGIN_EVENT_TABLE( SIMULATOR_FRAME, KIWAY_PLAYER )
|
|
EVT_MENU( wxID_EXIT, SIMULATOR_FRAME::onExit )
|
|
EVT_MENU( wxID_CLOSE, SIMULATOR_FRAME::onExit )
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
SIMULATOR_FRAME::SIMULATOR_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
|
|
KIWAY_PLAYER( aKiway, aParent, FRAME_SIMULATOR, _( "Simulator" ), wxDefaultPosition,
|
|
wxDefaultSize, wxDEFAULT_FRAME_STYLE, wxT( "simulator" ), unityScale ),
|
|
m_schematicFrame( nullptr ),
|
|
m_toolBar( nullptr ),
|
|
m_panel( nullptr ),
|
|
m_simFinished( false ),
|
|
m_workbookModified( false )
|
|
{
|
|
m_schematicFrame = (SCH_EDIT_FRAME*) Kiway().Player( FRAME_SCH, false );
|
|
wxASSERT( m_schematicFrame );
|
|
|
|
// Give an icon
|
|
wxIcon icon;
|
|
icon.CopyFromBitmap( KiBitmap( BITMAPS::simulator ) );
|
|
SetIcon( icon );
|
|
|
|
wxBoxSizer* mainSizer = new wxBoxSizer( wxVERTICAL );
|
|
SetSizer( mainSizer );
|
|
|
|
m_infoBar = new WX_INFOBAR( this );
|
|
mainSizer->Add( m_infoBar, 0, wxEXPAND, 0 );
|
|
|
|
m_toolBar = new ACTION_TOOLBAR( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
|
wxAUI_TB_DEFAULT_STYLE|wxAUI_TB_HORZ_LAYOUT|wxAUI_TB_PLAIN_BACKGROUND );
|
|
m_toolBar->Realize();
|
|
mainSizer->Add( m_toolBar, 0, wxEXPAND, 5 );
|
|
|
|
m_panel = new SIMULATOR_PANEL( this, m_schematicFrame );
|
|
mainSizer->Add( m_panel, 1, wxEXPAND, 5 );
|
|
|
|
m_simulator = SIMULATOR::CreateInstance( "ngspice" );
|
|
wxASSERT( m_simulator );
|
|
|
|
LoadSettings( config() );
|
|
|
|
NGSPICE_SIMULATOR_SETTINGS* settings =
|
|
dynamic_cast<NGSPICE_SIMULATOR_SETTINGS*>( m_simulator->Settings().get() );
|
|
|
|
wxCHECK2( settings, /* do nothing in release builds*/ );
|
|
|
|
if( settings && settings->GetWorkbookFilename().IsEmpty() )
|
|
settings->SetModelMode( NGSPICE_MODEL_MODE::LT_PSPICE );
|
|
|
|
m_simulator->Init();
|
|
|
|
m_reporter = new SIM_THREAD_REPORTER( this );
|
|
m_simulator->SetReporter( m_reporter );
|
|
|
|
m_circuitModel = std::make_shared<NGSPICE_CIRCUIT_MODEL>( &m_schematicFrame->Schematic(), this );
|
|
|
|
setupTools();
|
|
setupUIConditions();
|
|
|
|
ReCreateHToolbar();
|
|
ReCreateMenuBar();
|
|
|
|
Bind( wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( SIMULATOR_FRAME::onExit ), this,
|
|
wxID_EXIT );
|
|
|
|
Bind( EVT_SIM_UPDATE, &SIMULATOR_FRAME::onUpdateSim, this );
|
|
Bind( EVT_SIM_REPORT, &SIMULATOR_FRAME::onSimReport, this );
|
|
Bind( EVT_SIM_STARTED, &SIMULATOR_FRAME::onSimStarted, this );
|
|
Bind( EVT_SIM_FINISHED, &SIMULATOR_FRAME::onSimFinished, this );
|
|
|
|
// Ensure new items are taken in account by sizers:
|
|
Layout();
|
|
|
|
// resize the subwindows size. At least on Windows, calling wxSafeYield before
|
|
// resizing the subwindows forces the wxSplitWindows size events automatically generated
|
|
// by wxWidgets to be executed before our resize code.
|
|
// Otherwise, the changes made by setSubWindowsSashSize are overwritten by one these
|
|
// events
|
|
wxSafeYield();
|
|
m_panel->SetSubWindowsSashSize();
|
|
|
|
// Ensure the window is on top
|
|
Raise();
|
|
|
|
m_panel->InitWorkbook();
|
|
UpdateTitle();
|
|
}
|
|
|
|
|
|
SIMULATOR_FRAME::~SIMULATOR_FRAME()
|
|
{
|
|
NULL_REPORTER devnull;
|
|
|
|
m_simulator->Attach( nullptr, wxEmptyString, 0, devnull );
|
|
m_simulator->SetReporter( nullptr );
|
|
delete m_reporter;
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::setupTools()
|
|
{
|
|
// Create the manager
|
|
m_toolManager = new TOOL_MANAGER;
|
|
m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this );
|
|
|
|
m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager );
|
|
|
|
// Attach the events to the tool dispatcher
|
|
Bind( wxEVT_CHAR, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
|
|
Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher );
|
|
|
|
// Register tools
|
|
m_toolManager->RegisterTool( new COMMON_CONTROL );
|
|
m_toolManager->RegisterTool( new SIMULATOR_CONTROL );
|
|
m_toolManager->InitTools();
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::ShowChangedLanguage()
|
|
{
|
|
EDA_BASE_FRAME::ShowChangedLanguage();
|
|
|
|
UpdateTitle();
|
|
|
|
m_panel->ShowChangedLanguage();
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::LoadSettings( APP_SETTINGS_BASE* aCfg )
|
|
{
|
|
EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
|
|
wxASSERT( cfg );
|
|
|
|
if( cfg )
|
|
{
|
|
EDA_BASE_FRAME::LoadSettings( cfg );
|
|
m_panel->LoadSettings( cfg );
|
|
}
|
|
|
|
PROJECT_FILE& project = Prj().GetProjectFile();
|
|
|
|
NGSPICE* currentSim = dynamic_cast<NGSPICE*>( m_simulator.get() );
|
|
|
|
if( currentSim )
|
|
m_simulator->Settings() = project.m_SchematicSettings->m_NgspiceSimulatorSettings;
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
|
|
{
|
|
EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
|
|
wxASSERT( cfg );
|
|
|
|
if( cfg )
|
|
{
|
|
EDA_BASE_FRAME::SaveSettings( cfg );
|
|
m_panel->SaveSettings( cfg );
|
|
}
|
|
|
|
PROJECT_FILE& project = Prj().GetProjectFile();
|
|
|
|
if( project.m_SchematicSettings )
|
|
{
|
|
bool modified = project.m_SchematicSettings->m_NgspiceSimulatorSettings->SaveToFile();
|
|
|
|
if( m_schematicFrame && modified )
|
|
m_schematicFrame->OnModify();
|
|
}
|
|
}
|
|
|
|
|
|
WINDOW_SETTINGS* SIMULATOR_FRAME::GetWindowSettings( APP_SETTINGS_BASE* aCfg )
|
|
{
|
|
EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( aCfg );
|
|
wxASSERT( cfg );
|
|
|
|
return cfg ? &cfg->m_Simulator.window : nullptr;
|
|
}
|
|
|
|
|
|
wxString SIMULATOR_FRAME::GetCurrentSimCommand() const
|
|
{
|
|
if( m_panel->GetCurrentPlotPanel() )
|
|
return m_panel->GetCurrentPlotPanel()->GetSimCommand();
|
|
else
|
|
return m_circuitModel->GetSchTextSimCommand();
|
|
}
|
|
|
|
|
|
SIM_TYPE SIMULATOR_FRAME::GetCurrentSimType() const
|
|
{
|
|
return NGSPICE_CIRCUIT_MODEL::CommandToSimType( GetCurrentSimCommand() );
|
|
}
|
|
|
|
|
|
int SIMULATOR_FRAME::GetCurrentOptions() const
|
|
{
|
|
if( SIM_PLOT_PANEL_BASE* plotPanel = m_panel->GetCurrentPlotPanel() )
|
|
return plotPanel->GetSimOptions();
|
|
else
|
|
return NETLIST_EXPORTER_SPICE::OPTION_DEFAULT_FLAGS;
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::UpdateTitle()
|
|
{
|
|
bool unsaved = true;
|
|
bool readOnly = false;
|
|
wxString title;
|
|
wxFileName filename = Prj().AbsolutePath( m_simulator->Settings()->GetWorkbookFilename() );
|
|
|
|
if( filename.IsOk() && filename.FileExists() )
|
|
{
|
|
unsaved = false;
|
|
readOnly = !filename.IsFileWritable();
|
|
}
|
|
|
|
if( m_workbookModified )
|
|
title = wxT( "*" ) + filename.GetName();
|
|
else
|
|
title = filename.GetName();
|
|
|
|
if( readOnly )
|
|
title += wxS( " " ) + _( "[Read Only]" );
|
|
|
|
if( unsaved )
|
|
title += wxS( " " ) + _( "[Unsaved]" );
|
|
|
|
title += wxT( " \u2014 " ) + _( "Spice Simulator" );
|
|
|
|
SetTitle( title );
|
|
}
|
|
|
|
|
|
|
|
bool SIMULATOR_FRAME::LoadSimulator( const wxString& aSimCommand, unsigned aSimOptions )
|
|
{
|
|
wxString errors;
|
|
WX_STRING_REPORTER reporter( &errors );
|
|
|
|
if( !m_schematicFrame->ReadyToNetlist( _( "Simulator requires a fully annotated schematic." ) ) )
|
|
return false;
|
|
|
|
// If we are using the new connectivity, make sure that we do a full-rebuild
|
|
if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity )
|
|
m_schematicFrame->RecalculateConnections( nullptr, GLOBAL_CLEANUP );
|
|
|
|
if( !m_simulator->Attach( m_circuitModel, aSimCommand, aSimOptions, reporter ) )
|
|
{
|
|
DisplayErrorMessage( this, _( "Errors during netlist generation.\n\n" ) + errors );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::StartSimulation()
|
|
{
|
|
SIM_PLOT_PANEL_BASE* plotPanel = m_panel->GetCurrentPlotPanel();
|
|
|
|
if( !plotPanel )
|
|
return;
|
|
|
|
if( plotPanel->GetSimCommand().Upper().StartsWith( wxT( "FFT" ) ) )
|
|
{
|
|
wxString tranSpicePlot;
|
|
|
|
if( SIM_PLOT_PANEL_BASE* tranPlotPanel = m_panel->GetPlotPanel( ST_TRAN ) )
|
|
tranSpicePlot = tranPlotPanel->GetSpicePlotName();
|
|
|
|
if( tranSpicePlot.IsEmpty() )
|
|
{
|
|
DisplayErrorMessage( this, _( "You must run a TRAN simulation first; its results"
|
|
"will be used for the fast Fourier transform." ) );
|
|
}
|
|
else
|
|
{
|
|
m_simulator->Command( "setplot " + tranSpicePlot.ToStdString() );
|
|
|
|
wxArrayString commands = wxSplit( plotPanel->GetSimCommand(), '\n' );
|
|
|
|
for( const wxString& command : commands )
|
|
{
|
|
wxBusyCursor wait;
|
|
m_simulator->Command( command.ToStdString() );
|
|
}
|
|
|
|
plotPanel->SetSpicePlotName( m_simulator->CurrentPlotName() );
|
|
m_panel->OnSimRefresh( true );
|
|
|
|
#if 0
|
|
m_simulator->Command( "setplot" ); // Print available plots to console
|
|
m_simulator->Command( "display" ); // Print vectors in current plot to console
|
|
#endif
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( m_panel->GetPlotIndex( plotPanel ) == 0
|
|
&& m_circuitModel->GetSchTextSimCommand() != plotPanel->GetLastSchTextSimCommand() )
|
|
{
|
|
if( IsOK( this, _( "Schematic sheet simulation command directive has changed. "
|
|
"Do you wish to update the Simulation Command?" ) ) )
|
|
{
|
|
plotPanel->SetSimCommand( m_circuitModel->GetSchTextSimCommand() );
|
|
plotPanel->SetLastSchTextSimCommand( plotPanel->GetSimCommand() );
|
|
OnModify();
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !LoadSimulator( plotPanel->GetSimCommand(), plotPanel->GetSimOptions() ) )
|
|
return;
|
|
|
|
std::unique_lock<std::mutex> simulatorLock( m_simulator->GetMutex(), std::try_to_lock );
|
|
|
|
if( simulatorLock.owns_lock() )
|
|
{
|
|
m_panel->OnSimUpdate();
|
|
m_simulator->Run();
|
|
}
|
|
else
|
|
{
|
|
DisplayErrorMessage( this, _( "Another simulation is already running." ) );
|
|
}
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::NewPlotPanel( const wxString& aSimCommand, unsigned aOptions )
|
|
{
|
|
m_panel->NewPlotPanel( aSimCommand, aOptions );
|
|
}
|
|
|
|
|
|
const std::vector<wxString> SIMULATOR_FRAME::SimPlotVectors()
|
|
{
|
|
return m_panel->SimPlotVectors();
|
|
}
|
|
|
|
|
|
const std::vector<wxString> SIMULATOR_FRAME::Signals()
|
|
{
|
|
return m_panel->Signals();
|
|
}
|
|
|
|
|
|
const std::map<int, wxString>& SIMULATOR_FRAME::UserDefinedSignals()
|
|
{
|
|
return m_panel->UserDefinedSignals();
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::SetUserDefinedSignals( const std::map<int, wxString>& aSignals )
|
|
{
|
|
m_panel->SetUserDefinedSignals( aSignals );
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::AddVoltageTrace( const wxString& aNetName )
|
|
{
|
|
m_panel->AddTrace( aNetName, SPT_VOLTAGE );
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::AddCurrentTrace( const wxString& aDeviceName )
|
|
{
|
|
m_panel->AddTrace( aDeviceName, SPT_CURRENT );
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::AddTuner( const SCH_SHEET_PATH& aSheetPath, SCH_SYMBOL* aSymbol )
|
|
{
|
|
m_panel->AddTuner( aSheetPath, aSymbol );
|
|
}
|
|
|
|
|
|
SIM_PLOT_PANEL_BASE* SIMULATOR_FRAME::GetCurrentPlotPanel() const
|
|
{
|
|
return m_panel->GetCurrentPlotPanel();
|
|
}
|
|
|
|
|
|
bool SIMULATOR_FRAME::LoadWorkbook( const wxString& aPath )
|
|
{
|
|
if( m_panel->LoadWorkbook( aPath ) )
|
|
{
|
|
UpdateTitle();
|
|
|
|
// Successfully loading a workbook does not count as modifying it. Clear the modified
|
|
// flag after all the EVT_WORKBOOK_MODIFIED events have been processed.
|
|
CallAfter( [=]()
|
|
{
|
|
m_workbookModified = false;
|
|
} );
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SIMULATOR_FRAME::SaveWorkbook( const wxString& aPath )
|
|
{
|
|
if( m_panel->SaveWorkbook( aPath ) )
|
|
{
|
|
UpdateTitle();
|
|
m_workbookModified = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::ToggleDarkModePlots()
|
|
{
|
|
m_panel->ToggleDarkModePlots();
|
|
}
|
|
|
|
|
|
bool SIMULATOR_FRAME::EditSimCommand()
|
|
{
|
|
SIM_PLOT_PANEL_BASE* plotPanel = m_panel->GetCurrentPlotPanel();
|
|
DIALOG_SIM_COMMAND dlg( this, m_circuitModel, m_simulator->Settings() );
|
|
wxString errors;
|
|
WX_STRING_REPORTER reporter( &errors );
|
|
|
|
if( !plotPanel )
|
|
return false;
|
|
|
|
if( !m_circuitModel->ReadSchematicAndLibraries( NETLIST_EXPORTER_SPICE::OPTION_DEFAULT_FLAGS,
|
|
reporter ) )
|
|
{
|
|
DisplayErrorMessage( this, _( "Errors during netlist generation.\n\n" )
|
|
+ errors );
|
|
}
|
|
|
|
dlg.SetSimCommand( plotPanel->GetSimCommand() );
|
|
dlg.SetSimOptions( plotPanel->GetSimOptions() );
|
|
|
|
if( dlg.ShowModal() == wxID_OK )
|
|
{
|
|
plotPanel->SetSimCommand( dlg.GetSimCommand() );
|
|
plotPanel->SetSimOptions( dlg.GetSimOptions() );
|
|
m_panel->OnPlotSettingsChanged();
|
|
OnModify();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool SIMULATOR_FRAME::canCloseWindow( wxCloseEvent& aEvent )
|
|
{
|
|
if( m_workbookModified )
|
|
{
|
|
wxFileName filename = m_simulator->Settings()->GetWorkbookFilename();
|
|
|
|
if( filename.GetName().IsEmpty() )
|
|
{
|
|
if( Prj().GetProjectName().IsEmpty() )
|
|
filename.SetFullName( wxT( "noname.wbk" ) );
|
|
else
|
|
filename.SetFullName( Prj().GetProjectName() + wxT( ".wbk" ) );
|
|
}
|
|
|
|
return HandleUnsavedChanges( this, _( "Save changes to workbook?" ),
|
|
[&]() -> bool
|
|
{
|
|
return SaveWorkbook( Prj().AbsolutePath( filename.GetFullName() ) );
|
|
} );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::doCloseWindow()
|
|
{
|
|
if( m_simulator->IsRunning() )
|
|
m_simulator->Stop();
|
|
|
|
// Prevent memory leak on exit by deleting all simulation vectors
|
|
m_simulator->Clean();
|
|
|
|
// Cancel a running simProbe or simTune tool
|
|
m_schematicFrame->GetToolManager()->PostAction( ACTIONS::cancelInteractive );
|
|
|
|
SaveSettings( config() );
|
|
|
|
m_simulator->Settings() = nullptr;
|
|
|
|
Destroy();
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::setupUIConditions()
|
|
{
|
|
EDA_BASE_FRAME::setupUIConditions();
|
|
|
|
ACTION_MANAGER* mgr = m_toolManager->GetActionManager();
|
|
wxASSERT( mgr );
|
|
|
|
auto showGridCondition =
|
|
[this]( const SELECTION& aSel )
|
|
{
|
|
SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( GetCurrentPlotPanel() );
|
|
return plotPanel && plotPanel->IsGridShown();
|
|
};
|
|
|
|
auto showLegendCondition =
|
|
[this]( const SELECTION& aSel )
|
|
{
|
|
SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( GetCurrentPlotPanel() );
|
|
return plotPanel && plotPanel->IsLegendShown();
|
|
};
|
|
|
|
auto showDottedCondition =
|
|
[this]( const SELECTION& aSel )
|
|
{
|
|
SIM_PLOT_PANEL* plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( GetCurrentPlotPanel() );
|
|
return plotPanel && plotPanel->GetDottedSecondary();
|
|
};
|
|
|
|
auto darkModePlotCondition =
|
|
[this]( const SELECTION& aSel )
|
|
{
|
|
return m_panel->DarkModePlots();
|
|
};
|
|
|
|
auto simRunning =
|
|
[this]( const SELECTION& aSel )
|
|
{
|
|
return m_simulator && m_simulator->IsRunning();
|
|
};
|
|
|
|
auto simFinished =
|
|
[this]( const SELECTION& aSel )
|
|
{
|
|
return m_simFinished;
|
|
};
|
|
|
|
auto haveSim =
|
|
[this]( const SELECTION& aSel )
|
|
{
|
|
return GetCurrentPlotPanel() != nullptr;
|
|
};
|
|
|
|
auto havePlot =
|
|
[this]( const SELECTION& aSel )
|
|
{
|
|
return dynamic_cast<SIM_PLOT_PANEL*>( GetCurrentPlotPanel() ) != nullptr;
|
|
};
|
|
|
|
#define ENABLE( x ) ACTION_CONDITIONS().Enable( x )
|
|
#define CHECK( x ) ACTION_CONDITIONS().Check( x )
|
|
|
|
mgr->SetConditions( EE_ACTIONS::openWorkbook, ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
|
|
mgr->SetConditions( EE_ACTIONS::saveWorkbook, ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
|
|
mgr->SetConditions( EE_ACTIONS::saveWorkbookAs, ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
|
|
|
|
mgr->SetConditions( EE_ACTIONS::exportPlotAsPNG, ENABLE( havePlot ) );
|
|
mgr->SetConditions( EE_ACTIONS::exportPlotAsCSV, ENABLE( havePlot ) );
|
|
|
|
mgr->SetConditions( EE_ACTIONS::toggleGrid, CHECK( showGridCondition ) );
|
|
mgr->SetConditions( EE_ACTIONS::toggleLegend, CHECK( showLegendCondition ) );
|
|
mgr->SetConditions( EE_ACTIONS::toggleDottedSecondary, CHECK( showDottedCondition ) );
|
|
mgr->SetConditions( EE_ACTIONS::toggleDarkModePlots, CHECK( darkModePlotCondition ) );
|
|
|
|
mgr->SetConditions( EE_ACTIONS::newPlot, ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
|
|
mgr->SetConditions( EE_ACTIONS::simCommand, ENABLE( haveSim ) );
|
|
mgr->SetConditions( EE_ACTIONS::runSimulation, ENABLE( !simRunning ) );
|
|
mgr->SetConditions( EE_ACTIONS::stopSimulation, ENABLE( simRunning ) );
|
|
mgr->SetConditions( EE_ACTIONS::simProbe, ENABLE( simFinished ) );
|
|
mgr->SetConditions( EE_ACTIONS::simTune, ENABLE( simFinished ) );
|
|
mgr->SetConditions( EE_ACTIONS::showNetlist, ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
|
|
|
|
#undef CHECK
|
|
#undef ENABLE
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::onSimStarted( wxCommandEvent& aEvent )
|
|
{
|
|
SetCursor( wxCURSOR_ARROWWAIT );
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::onSimFinished( wxCommandEvent& aEvent )
|
|
{
|
|
// Sometimes (for instance with a directive like wrdata my_file.csv "my_signal")
|
|
// the simulator is in idle state (simulation is finished), but still running, during
|
|
// the time the file is written. So gives a slice of time to fully finish the work:
|
|
if( m_simulator->IsRunning() )
|
|
{
|
|
int max_time = 40; // For a max timeout = 2s
|
|
|
|
do
|
|
{
|
|
wxMilliSleep( 50 );
|
|
wxYield();
|
|
|
|
if( max_time )
|
|
max_time--;
|
|
|
|
} while( max_time && m_simulator->IsRunning() );
|
|
}
|
|
// Is a warning message useful if the simulatior is still running?
|
|
|
|
SCHEMATIC& schematic = m_schematicFrame->Schematic();
|
|
schematic.ClearOperatingPoints();
|
|
|
|
m_simFinished = true;
|
|
|
|
m_panel->OnSimRefresh( true );
|
|
|
|
m_schematicFrame->RefreshOperatingPointDisplay();
|
|
m_schematicFrame->GetCanvas()->Refresh();
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::onUpdateSim( wxCommandEvent& aEvent )
|
|
{
|
|
static bool updateInProgress = false;
|
|
|
|
// skip update when events are triggered too often and previous call didn't end yet
|
|
if( updateInProgress )
|
|
return;
|
|
|
|
updateInProgress = true;
|
|
|
|
if( m_simulator->IsRunning() )
|
|
m_simulator->Stop();
|
|
|
|
std::unique_lock<std::mutex> simulatorLock( m_simulator->GetMutex(), std::try_to_lock );
|
|
|
|
if( simulatorLock.owns_lock() )
|
|
{
|
|
m_panel->OnSimUpdate();
|
|
m_simulator->Run();
|
|
}
|
|
else
|
|
{
|
|
DisplayErrorMessage( this, _( "Another simulation is already running." ) );
|
|
}
|
|
|
|
updateInProgress = false;
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::onSimReport( wxCommandEvent& aEvent )
|
|
{
|
|
m_panel->OnSimReport( aEvent.GetString() );
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::onExit( wxCommandEvent& aEvent )
|
|
{
|
|
if( aEvent.GetId() == wxID_EXIT )
|
|
Kiway().OnKiCadExit();
|
|
|
|
if( aEvent.GetId() == wxID_CLOSE )
|
|
Close( false );
|
|
}
|
|
|
|
|
|
void SIMULATOR_FRAME::OnModify()
|
|
{
|
|
KIWAY_PLAYER::OnModify();
|
|
m_workbookModified = true;
|
|
}
|
|
|
|
|
|
wxDEFINE_EVENT( EVT_SIM_UPDATE, wxCommandEvent );
|
|
wxDEFINE_EVENT( EVT_SIM_REPORT, wxCommandEvent );
|
|
|
|
wxDEFINE_EVENT( EVT_SIM_STARTED, wxCommandEvent );
|
|
wxDEFINE_EVENT( EVT_SIM_FINISHED, wxCommandEvent );
|