kicad-source/eeschema/sim/simulator_frame.cpp
Jeff Young 492ef62053 ADDED support to save contributions from all noise generators.
You must first check the checkbox in the Simulation Command dialog,
after which the signals will appear in the User Defined Signals
autocomplete lists.
2023-07-04 11:05:10 +01:00

819 lines
23 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_lastSimPlot( 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::onSimUpdate, 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, 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->GetCurrentPlotWindow() )
return m_panel->GetCurrentPlotWindow()->GetSimCommand();
else
return m_circuitModel->GetSchTextSimCommand();
}
SIM_TYPE SIMULATOR_FRAME::GetCurrentSimType() const
{
return NGSPICE_CIRCUIT_MODEL::CommandToSimType( GetCurrentSimCommand() );
}
int SIMULATOR_FRAME::GetCurrentOptions() const
{
if( m_panel->GetCurrentPlotWindow() )
return m_panel->GetCurrentPlotWindow()->GetSimOptions();
else
return m_circuitModel->GetSimOptions();
}
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()
{
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, reporter ) )
{
DisplayErrorMessage( this, _( "Errors during netlist generation.\n\n" ) + errors );
return false;
}
return true;
}
void SIMULATOR_FRAME::StartSimulation()
{
if( m_circuitModel->CommandToSimType( GetCurrentSimCommand() ) == ST_UNKNOWN )
{
if( !EditSimCommand() )
return;
if( m_circuitModel->CommandToSimType( GetCurrentSimCommand() ) == ST_UNKNOWN )
return;
}
wxString schTextSimCommand = m_circuitModel->GetSchTextSimCommand();
SIM_TYPE schTextSimType = NGSPICE_CIRCUIT_MODEL::CommandToSimType( schTextSimCommand );
SIM_PLOT_PANEL_BASE* plotWindow = m_panel->GetCurrentPlotWindow();
if( !plotWindow )
{
NewPlotPanel( schTextSimCommand, m_circuitModel->GetSimOptions() );
OnModify();
}
else
{
m_circuitModel->SetSimCommandOverride( plotWindow->GetSimCommand() );
if( plotWindow->GetType() == schTextSimType
&& schTextSimCommand != m_circuitModel->GetLastSchTextSimCommand() )
{
if( IsOK( this, _( "Schematic sheet simulation command directive has changed. "
"Do you wish to update the Simulation Command?" ) ) )
{
m_circuitModel->SetSimCommandOverride( wxEmptyString );
plotWindow->SetSimCommand( schTextSimCommand );
OnModify();
}
}
}
m_circuitModel->SetSimOptions( GetCurrentOptions() );
if( !LoadSimulator() )
return;
std::unique_lock<std::mutex> simulatorLock( m_simulator->GetMutex(), std::try_to_lock );
if( simulatorLock.owns_lock() )
{
wxBusyCursor toggle;
m_panel->OnSimUpdate();
// Prevents memory leak on succeding simulations by deleting old vectors
m_simulator->Clean();
m_simulator->Run();
}
else
{
DisplayErrorMessage( this, _( "Another simulation is already running." ) );
}
}
void SIMULATOR_FRAME::NewPlotPanel( const wxString& aSimCommand, int aOptions )
{
m_panel->NewPlotPanel( aSimCommand, aOptions );
}
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* SIMULATOR_FRAME::GetCurrentPlot() const
{
return m_panel->GetCurrentPlot();
}
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* plotPanelWindow = m_panel->GetCurrentPlotWindow();
DIALOG_SIM_COMMAND dlg( this, m_circuitModel, m_simulator->Settings() );
wxString errors;
WX_STRING_REPORTER reporter( &errors );
if( !m_circuitModel->ReadSchematicAndLibraries( NETLIST_EXPORTER_SPICE::OPTION_DEFAULT_FLAGS,
reporter ) )
{
DisplayErrorMessage( this, _( "Errors during netlist generation.\n\n" )
+ errors );
}
if( m_panel->GetPlotIndex( plotPanelWindow ) != wxNOT_FOUND )
{
dlg.SetSimCommand( plotPanelWindow->GetSimCommand() );
dlg.SetSimOptions( plotPanelWindow->GetSimOptions() );
}
else
{
dlg.SetSimOptions( NETLIST_EXPORTER_SPICE::OPTION_DEFAULT_FLAGS );
}
if( dlg.ShowModal() == wxID_OK )
{
wxString oldCommand;
if( m_panel->GetPlotIndex( plotPanelWindow ) != wxNOT_FOUND )
oldCommand = plotPanelWindow->GetSimCommand();
else
oldCommand = wxString();
const wxString& newCommand = dlg.GetSimCommand();
int newOptions = dlg.GetSimOptions();
SIM_TYPE newSimType = NGSPICE_CIRCUIT_MODEL::CommandToSimType( newCommand );
if( !plotPanelWindow )
{
m_circuitModel->SetSimCommandOverride( newCommand );
m_circuitModel->SetSimOptions( newOptions );
NewPlotPanel( newCommand, newOptions );
}
// If it is a new simulation type, open a new plot. For the DC sim, check if sweep
// source type has changed (char 4 will contain 'v', 'i', 'r' or 't'.
else if( plotPanelWindow->GetType() != newSimType
|| ( newSimType == ST_DC
&& oldCommand.Lower().GetChar( 4 ) != newCommand.Lower().GetChar( 4 ) ) )
{
NewPlotPanel( newCommand, newOptions );
}
else
{
if( m_panel->GetPlotIndex( plotPanelWindow ) == 0 )
m_circuitModel->SetSimCommandOverride( newCommand );
// Update simulation command in the current plot
plotPanelWindow->SetSimCommand( newCommand );
plotPanelWindow->SetSimOptions( newOptions );
}
OnModify();
m_simulator->Init();
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* plot = GetCurrentPlot();
return plot && plot->IsGridShown();
};
auto showLegendCondition =
[this]( const SELECTION& aSel )
{
SIM_PLOT_PANEL* plot = GetCurrentPlot();
return plot && plot->IsLegendShown();
};
auto showDottedCondition =
[this]( const SELECTION& aSel )
{
SIM_PLOT_PANEL* plot = GetCurrentPlot();
return plot && plot->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 havePlot =
[this]( const SELECTION& aSel )
{
return GetCurrentPlot() != 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::simCommand, ENABLE( SELECTION_CONDITIONS::ShowAlways ) );
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 )
{
SetCursor( wxCURSOR_ARROW );
SIM_TYPE simType = m_circuitModel->GetSimType();
if( simType == ST_UNKNOWN )
return;
// 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();
m_lastSimPlot = m_panel->GetCurrentPlotWindow();
}
void SIMULATOR_FRAME::onSimUpdate( 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();
if( m_panel->GetCurrentPlotWindow() != m_lastSimPlot )
{
// We need to rerun simulation, as the simulator currently stores results for another
// plot
StartSimulation();
}
else
{
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 );