kicad/eeschema/sim/simulator_frame.cpp

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->GetSimType() == 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->GetSimType() != 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 );