Allow opening of workbook when simulation frame is opened.

This involved splitting creation of traces from setting of trace
data.

Also renamed SIM_WORKBOOK to SIM_NOTEBOOK.  This class is a subclass
of wxAiuNotebook and represents the collection of simulation plot tabs.
It is NOT the same thing as a simulation workbook, which contains other
stuff such as measurements, plotted signals, colours, etc.

This also removes a bunch of "friend" declarations.
This commit is contained in:
Jeff Young 2023-02-12 14:55:00 +00:00
parent 78746b77c6
commit 83dd06e5d1
11 changed files with 328 additions and 409 deletions

View File

@ -354,13 +354,13 @@ if( KICAD_SPICE )
tools/simulator_control.cpp tools/simulator_control.cpp
sim/ngspice_circuit_model.cpp sim/ngspice_circuit_model.cpp
sim/ngspice.cpp sim/ngspice.cpp
sim/sim_notebook.cpp
sim/sim_panel_base.cpp sim/sim_panel_base.cpp
sim/sim_plot_colors.cpp sim/sim_plot_colors.cpp
sim/sim_plot_frame.cpp sim/sim_plot_frame.cpp
sim/sim_plot_frame_base.cpp sim/sim_plot_frame_base.cpp
sim/sim_plot_panel.cpp sim/sim_plot_panel.cpp
sim/sim_property.cpp sim/sim_property.cpp
sim/sim_workbook.cpp
sim/spice_simulator.cpp sim/spice_simulator.cpp
sim/spice_value.cpp sim/spice_value.cpp
sim/toolbars_sim_plot_frame.cpp sim/toolbars_sim_plot_frame.cpp

View File

@ -22,28 +22,25 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#include <sim/sim_workbook.h> #include <sim/sim_notebook.h>
SIM_WORKBOOK::SIM_WORKBOOK() : wxAuiNotebook() SIM_NOTEBOOK::SIM_NOTEBOOK() :
{ wxAuiNotebook()
m_modified = false; { }
}
SIM_WORKBOOK::SIM_WORKBOOK( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos, const wxSize& SIM_NOTEBOOK::SIM_NOTEBOOK( wxWindow* aParent, wxWindowID aId, const wxPoint& aPos, const wxSize&
aSize, long aStyle ) : aSize, long aStyle ) :
wxAuiNotebook( aParent, aId, aPos, aSize, aStyle ) wxAuiNotebook( aParent, aId, aPos, aSize, aStyle )
{ { }
m_modified = false;
}
bool SIM_WORKBOOK::AddPage( wxWindow* page, const wxString& caption, bool select, const wxBitmap& bitmap ) bool SIM_NOTEBOOK::AddPage( wxWindow* page, const wxString& caption, bool select, const wxBitmap& bitmap )
{ {
if( wxAuiNotebook::AddPage( page, caption, select, bitmap ) ) if( wxAuiNotebook::AddPage( page, caption, select, bitmap ) )
{ {
setModified(); wxPostEvent( GetParent(), wxCommandEvent( EVT_WORKBOOK_MODIFIED ) );
return true; return true;
} }
@ -51,11 +48,11 @@ bool SIM_WORKBOOK::AddPage( wxWindow* page, const wxString& caption, bool select
} }
bool SIM_WORKBOOK::AddPage( wxWindow* page, const wxString& text, bool select, int imageId ) bool SIM_NOTEBOOK::AddPage( wxWindow* page, const wxString& text, bool select, int imageId )
{ {
if( wxAuiNotebook::AddPage( page, text, select, imageId ) ) if( wxAuiNotebook::AddPage( page, text, select, imageId ) )
{ {
setModified(); wxPostEvent( GetParent(), wxCommandEvent( EVT_WORKBOOK_MODIFIED ) );
return true; return true;
} }
@ -63,11 +60,11 @@ bool SIM_WORKBOOK::AddPage( wxWindow* page, const wxString& text, bool select, i
} }
bool SIM_WORKBOOK::DeleteAllPages() bool SIM_NOTEBOOK::DeleteAllPages()
{ {
if( wxAuiNotebook::DeleteAllPages() ) if( wxAuiNotebook::DeleteAllPages() )
{ {
setModified(); wxPostEvent( GetParent(), wxCommandEvent( EVT_WORKBOOK_MODIFIED ) );
return true; return true;
} }
@ -75,11 +72,11 @@ bool SIM_WORKBOOK::DeleteAllPages()
} }
bool SIM_WORKBOOK::DeletePage( size_t page ) bool SIM_NOTEBOOK::DeletePage( size_t page )
{ {
if( wxAuiNotebook::DeletePage( page ) ) if( wxAuiNotebook::DeletePage( page ) )
{ {
setModified(); wxPostEvent( GetParent(), wxCommandEvent( EVT_WORKBOOK_MODIFIED ) );
return true; return true;
} }
@ -87,45 +84,4 @@ bool SIM_WORKBOOK::DeletePage( size_t page )
} }
bool SIM_WORKBOOK::AddTrace( SIM_PLOT_PANEL* aPlotPanel, const wxString& aTitle,
const wxString& aName, int aPoints, const double* aX, const double* aY,
SIM_TRACE_TYPE aType )
{
if( aPoints && aPlotPanel->addTrace( aTitle, aName, aPoints, aX, aY, aType ) )
{
setModified();
return true;
}
return false;
}
bool SIM_WORKBOOK::DeleteTrace( SIM_PLOT_PANEL* aPlotPanel, const wxString& aName )
{
if( aPlotPanel->deleteTrace( aName ) )
{
setModified();
return true;
}
return false;
}
void SIM_WORKBOOK::ClrModified()
{
m_modified = false;
wxPostEvent( GetParent(), wxCommandEvent( EVT_WORKBOOK_CLR_MODIFIED ) );
}
void SIM_WORKBOOK::setModified()
{
m_modified = true;
wxPostEvent( GetParent(), wxCommandEvent( EVT_WORKBOOK_MODIFIED ) );
}
wxDEFINE_EVENT( EVT_WORKBOOK_MODIFIED, wxCommandEvent ); wxDEFINE_EVENT( EVT_WORKBOOK_MODIFIED, wxCommandEvent );
wxDEFINE_EVENT( EVT_WORKBOOK_CLR_MODIFIED, wxCommandEvent );

View File

@ -22,69 +22,32 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#ifndef __SIM_WORKBOOK__ #ifndef SIM_NOTEBOOK_H
#define __SIM_WORKBOOK__ #define SIM_NOTEBOOK_H
#include <dialog_sim_command.h> #include <dialog_sim_command.h>
#include <sim/sim_panel_base.h> #include <sim/sim_panel_base.h>
#include <sim/sim_plot_panel.h> #include <sim/sim_plot_panel.h>
class SIM_WORKBOOK : public wxAuiNotebook class SIM_NOTEBOOK : public wxAuiNotebook
{ {
public: public:
SIM_WORKBOOK(); SIM_NOTEBOOK();
SIM_WORKBOOK( wxWindow* aParent, wxWindowID aId=wxID_ANY, const wxPoint& SIM_NOTEBOOK( wxWindow* aParent, wxWindowID aId = wxID_ANY,
aPos=wxDefaultPosition, const wxSize& aSize=wxDefaultSize, long const wxPoint& aPos = wxDefaultPosition, const wxSize& aSize = wxDefaultSize,
aStyle=wxAUI_NB_DEFAULT_STYLE ); long aStyle = wxAUI_NB_DEFAULT_STYLE );
// Methods from wxAuiNotebook // Methods from wxAuiNotebook
bool AddPage( wxWindow* aPage, const wxString& aCaption, bool aSelect=false, const wxBitmap& aBitmap=wxNullBitmap ); bool AddPage( wxWindow* aPage, const wxString& aCaption, bool aSelect=false,
const wxBitmap& aBitmap = wxNullBitmap );
bool AddPage( wxWindow* aPage, const wxString& aText, bool aSelect, int aImageId ) override; bool AddPage( wxWindow* aPage, const wxString& aText, bool aSelect, int aImageId ) override;
bool DeleteAllPages() override; bool DeleteAllPages() override;
bool DeletePage( size_t aPage ) override; bool DeletePage( size_t aPage ) override;
// Custom methods
bool AddTrace( SIM_PLOT_PANEL* aPlotPanel, const wxString& aTitle, const wxString& aName,
int aPoints, const double* aX, const double* aY, SIM_TRACE_TYPE aType );
bool DeleteTrace( SIM_PLOT_PANEL* aPlotPanel, const wxString& aName );
void SetSimCommand( SIM_PANEL_BASE* aPlotPanel, const wxString& aSimCommand )
{
aPlotPanel->setSimCommand( aSimCommand );
setModified();
}
const wxString& GetSimCommand( const SIM_PANEL_BASE* aPlotPanel )
{
return aPlotPanel->getSimCommand();
}
void SetSimOptions( SIM_PANEL_BASE* aPlotPanel, int aOptions )
{
aPlotPanel->setSimOptions( aOptions );
setModified();
}
int GetSimOptions( const SIM_PANEL_BASE* aPlotPanel )
{
return aPlotPanel->getSimOptions();
}
void ClrModified();
bool IsModified() const { return m_modified; }
private:
void setModified();
///< Dirty bit, indicates something in the workbook has changed
bool m_modified;
}; };
wxDECLARE_EVENT( EVT_WORKBOOK_MODIFIED, wxCommandEvent ); wxDECLARE_EVENT( EVT_WORKBOOK_MODIFIED, wxCommandEvent );
wxDECLARE_EVENT( EVT_WORKBOOK_CLR_MODIFIED, wxCommandEvent );
#endif // __SIM_WORKBOOK__ #endif // SIM_NOTEBOOK_H

View File

@ -35,8 +35,6 @@
class SIM_PANEL_BASE : public wxWindow class SIM_PANEL_BASE : public wxWindow
{ {
friend class SIM_WORKBOOK;
public: public:
SIM_PANEL_BASE(); SIM_PANEL_BASE();
SIM_PANEL_BASE( const wxString& aCommand, int aOptions, wxWindow* parent, wxWindowID id, SIM_PANEL_BASE( const wxString& aCommand, int aOptions, wxWindow* parent, wxWindowID id,
@ -50,14 +48,8 @@ public:
SIM_TYPE GetType() const; SIM_TYPE GetType() const;
protected: const wxString& GetSimCommand() const { return m_simCommand; }
// We use `protected` here because members should be accessible from outside only through a void SetSimCommand( const wxString& aSimCommand )
// workbook object, to prevent anyone from modifying the state without its knowledge. Otherwise
// we risk some things not getting saved.
const wxString& getSimCommand() const { return m_simCommand; }
void setSimCommand( const wxString& aSimCommand )
{ {
wxCHECK_RET( GetType() == NGSPICE_CIRCUIT_MODEL::CommandToSimType( aSimCommand ), wxCHECK_RET( GetType() == NGSPICE_CIRCUIT_MODEL::CommandToSimType( aSimCommand ),
"Cannot change the type of simulation of the existing plot panel" ); "Cannot change the type of simulation of the existing plot panel" );
@ -65,8 +57,8 @@ protected:
m_simCommand = aSimCommand; m_simCommand = aSimCommand;
} }
const int getSimOptions() const { return m_simOptions; } const int GetSimOptions() const { return m_simOptions; }
void setSimOptions( int aOptions ) { m_simOptions = aOptions; } void SetSimOptions( int aOptions ) { m_simOptions = aOptions; }
private: private:
wxString m_simCommand; wxString m_simCommand;

View File

@ -330,7 +330,8 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_darkMode( true ), m_darkMode( true ),
m_plotNumber( 0 ), m_plotNumber( 0 ),
m_simFinished( false ), m_simFinished( false ),
m_outputCounter( 1 ) m_outputCounter( 1 ),
m_workbookModified( false )
{ {
SetKiway( this, aKiway ); SetKiway( this, aKiway );
@ -415,12 +416,11 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
Bind( EVT_SIM_FINISHED, &SIM_PLOT_FRAME::onSimFinished, this ); Bind( EVT_SIM_FINISHED, &SIM_PLOT_FRAME::onSimFinished, this );
Bind( EVT_SIM_CURSOR_UPDATE, &SIM_PLOT_FRAME::onCursorUpdate, this ); Bind( EVT_SIM_CURSOR_UPDATE, &SIM_PLOT_FRAME::onCursorUpdate, this );
Bind( EVT_WORKBOOK_MODIFIED, &SIM_PLOT_FRAME::onWorkbookModified, this ); Bind( EVT_WORKBOOK_MODIFIED, &SIM_PLOT_FRAME::onNotebookModified, this );
Bind( EVT_WORKBOOK_CLR_MODIFIED, &SIM_PLOT_FRAME::onWorkbookClrModified, this );
#ifndef wxHAS_NATIVE_TABART #ifndef wxHAS_NATIVE_TABART
// Default non-native tab art has ugly gradients we don't want // Default non-native tab art has ugly gradients we don't want
m_workbook->SetArtProvider( new wxAuiSimpleTabArt() ); m_plotNotebook->SetArtProvider( new wxAuiSimpleTabArt() );
#endif #endif
// Ensure new items are taken in account by sizers: // Ensure new items are taken in account by sizers:
@ -482,16 +482,16 @@ void SIM_PLOT_FRAME::ShowChangedLanguage()
updateTitle(); updateTitle();
for( int ii = 0; ii < (int) m_workbook->GetPageCount(); ++ii ) for( int ii = 0; ii < (int) m_plotNotebook->GetPageCount(); ++ii )
{ {
SIM_PANEL_BASE* plot = dynamic_cast<SIM_PLOT_PANEL*>( m_workbook->GetPage( ii ) ); SIM_PANEL_BASE* plot = dynamic_cast<SIM_PLOT_PANEL*>( m_plotNotebook->GetPage( ii ) );
plot->OnLanguageChanged(); plot->OnLanguageChanged();
wxString pageTitle( m_simulator->TypeToName( plot->GetType(), true ) ); wxString pageTitle( m_simulator->TypeToName( plot->GetType(), true ) );
pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), ii+1 /* 1-based */ ) ); pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), ii+1 /* 1-based */ ) );
m_workbook->SetPageText( ii, pageTitle ); m_plotNotebook->SetPageText( ii, pageTitle );
} }
m_filter->SetHint( _( "Filter" ) ); m_filter->SetHint( _( "Filter" ) );
@ -582,19 +582,14 @@ WINDOW_SETTINGS* SIM_PLOT_FRAME::GetWindowSettings( APP_SETTINGS_BASE* aCfg )
void SIM_PLOT_FRAME::initWorkbook() void SIM_PLOT_FRAME::initWorkbook()
{ {
// Removed for the time being. We cannot run the simulation on simulator launch, as it may if( !m_simulator->Settings()->GetWorkbookFilename().IsEmpty() )
// take a lot of time, confusing the user.
// TODO: Change workbook loading routines so that they don't run the simulation until the user
// initiates it.
/*if( !m_simulator->Settings()->GetWorkbookFilename().IsEmpty() )
{ {
wxFileName filename = m_simulator->Settings()->GetWorkbookFilename(); wxFileName filename = m_simulator->Settings()->GetWorkbookFilename();
filename.SetPath( Prj().GetProjectPath() ); filename.SetPath( Prj().GetProjectPath() );
if( !loadWorkbook( filename.GetFullPath() ) ) if( !LoadWorkbook( filename.GetFullPath() ) )
m_simulator->Settings()->SetWorkbookFilename( "" ); m_simulator->Settings()->SetWorkbookFilename( "" );
}*/ }
} }
@ -614,7 +609,7 @@ void SIM_PLOT_FRAME::updateTitle()
readOnly = !filename.IsFileWritable(); readOnly = !filename.IsFileWritable();
} }
if( m_workbook->IsModified() ) if( m_workbookModified )
title = wxT( "*" ) + filename.GetName(); title = wxT( "*" ) + filename.GetName();
else else
title = filename.GetName(); title = filename.GetName();
@ -721,6 +716,92 @@ void SIM_PLOT_FRAME::rebuildSignalsGrid( wxString aFilter )
} }
void SIM_PLOT_FRAME::rebuildSignalsList()
{
m_signals.clear();
int options = GetCurrentOptions();
SIM_TYPE simType = NGSPICE_CIRCUIT_MODEL::CommandToSimType( GetCurrentSimCommand() );
wxString unconnected = wxString( wxS( "unconnected-(" ) );
unconnected.Replace( '(', '_' ); // Convert to SPICE markup
if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES )
{
for( const std::string& net : m_circuitModel->GetNets() )
{
// netnames are escaped (can contain "{slash}" for '/') Unscape them:
wxString netname = UnescapeString( net );
if( netname == "GND" || netname == "0" || netname.StartsWith( unconnected ) )
continue;
if( simType == ST_AC )
{
m_signals.push_back( wxString::Format( _( "V(%s) (gain)" ), netname ) );
m_signals.push_back( wxString::Format( _( "V(%s) (phase)" ), netname ) );
}
else
{
m_signals.push_back( wxString::Format( "V(%s)", netname ) );
}
}
}
if( ( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS )
&& ( simType == ST_TRANSIENT || simType == ST_DC ) )
{
for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
{
// Add all possible currents for the device.
for( const std::string& name : item.model->SpiceGenerator().CurrentNames( item ) )
m_signals.push_back( name );
}
}
if( ( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS )
&& ( simType == ST_TRANSIENT || simType == ST_DC ) )
{
for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
{
if( item.model->GetPinCount() >= 2 )
{
wxString name = item.model->SpiceGenerator().ItemName( item );
m_signals.push_back( wxString::Format( wxS( "P(%s)" ), name ) );
}
}
}
// Add .PROBE directives
for( const wxString& directive : m_circuitModel->GetDirectives() )
{
wxString directiveParams;
if( directive.Upper().StartsWith( wxS( ".PROBE" ), &directiveParams ) )
m_signals.push_back( directiveParams.Trim( false ) );
}
}
bool SIM_PLOT_FRAME::LoadSimulator()
{
wxString errors;
WX_STRING_REPORTER reporter( &errors );
if( !m_schematicFrame->ReadyToNetlist( _( "Simulator requires a fully annotated schematic." ) ) )
return false;
if( !m_simulator->Attach( m_circuitModel, reporter ) )
{
DisplayErrorMessage( this, _( "Errors during netlist generation.\n\n" )
+ errors );
return false;
}
return true;
}
void SIM_PLOT_FRAME::StartSimulation() void SIM_PLOT_FRAME::StartSimulation()
{ {
if( m_circuitModel->CommandToSimType( GetCurrentSimCommand() ) == ST_UNKNOWN ) if( m_circuitModel->CommandToSimType( GetCurrentSimCommand() ) == ST_UNKNOWN )
@ -739,11 +820,11 @@ void SIM_PLOT_FRAME::StartSimulation()
if( !plotWindow ) if( !plotWindow )
{ {
plotWindow = NewPlotPanel( schTextSimCommand, m_circuitModel->GetSimOptions() ); plotWindow = NewPlotPanel( schTextSimCommand, m_circuitModel->GetSimOptions() );
m_workbook->SetSimCommand( plotWindow, schTextSimCommand ); m_workbookModified = true;
} }
else else
{ {
m_circuitModel->SetSimCommandOverride( m_workbook->GetSimCommand( plotWindow ) ); m_circuitModel->SetSimCommandOverride( plotWindow->GetSimCommand() );
if( plotWindow->GetType() == schTextSimType if( plotWindow->GetType() == schTextSimType
&& schTextSimCommand != m_circuitModel->GetLastSchTextSimCommand() ) && schTextSimCommand != m_circuitModel->GetLastSchTextSimCommand() )
@ -752,95 +833,27 @@ void SIM_PLOT_FRAME::StartSimulation()
"Do you wish to update the Simulation Command?" ) ) ) "Do you wish to update the Simulation Command?" ) ) )
{ {
m_circuitModel->SetSimCommandOverride( wxEmptyString ); m_circuitModel->SetSimCommandOverride( wxEmptyString );
m_workbook->SetSimCommand( plotWindow, schTextSimCommand ); plotWindow->SetSimCommand( schTextSimCommand );
m_workbookModified = true;
} }
} }
} }
m_circuitModel->SetSimOptions( GetCurrentOptions() ); m_circuitModel->SetSimOptions( GetCurrentOptions() );
wxString errors; if( !LoadSimulator() )
WX_STRING_REPORTER reporter( &errors );
if( !m_schematicFrame->ReadyToNetlist( _( "Simulator requires a fully annotated schematic." ) )
|| !m_simulator->Attach( m_circuitModel, reporter ) )
{
DisplayErrorMessage( this, _( "Errors during netlist generation; simulation aborted.\n\n" )
+ errors );
return; return;
}
rebuildSignalsList();
rebuildSignalsGrid( m_filter->GetValue() );
std::unique_lock<std::mutex> simulatorLock( m_simulator->GetMutex(), std::try_to_lock ); std::unique_lock<std::mutex> simulatorLock( m_simulator->GetMutex(), std::try_to_lock );
if( simulatorLock.owns_lock() ) if( simulatorLock.owns_lock() )
{ {
wxBusyCursor toggle; wxBusyCursor toggle;
wxString unconnected = wxString( wxS( "unconnected-(" ) );
unconnected.Replace( '(', '_' ); // Convert to SPICE markup
m_simConsole->Clear(); m_simConsole->Clear();
m_signals.clear();
int options = m_circuitModel->GetSimOptions();
SIM_TYPE simType = m_circuitModel->GetSimType();
if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES )
{
for( const std::string& net : m_circuitModel->GetNets() )
{
// netnames are escaped (can contain "{slash}" for '/') Unscape them:
wxString netname = UnescapeString( net );
if( netname == "GND" || netname == "0" || netname.StartsWith( unconnected ) )
continue;
if( simType == ST_AC )
{
m_signals.push_back( wxString::Format( _( "V(%s) (gain)" ), netname ) );
m_signals.push_back( wxString::Format( _( "V(%s) (phase)" ), netname ) );
}
else
{
m_signals.push_back( wxString::Format( "V(%s)", netname ) );
}
}
}
if( ( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS )
&& ( simType == ST_TRANSIENT || simType == ST_DC ) )
{
for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
{
// Add all possible currents for the device.
for( const std::string& name : item.model->SpiceGenerator().CurrentNames( item ) )
m_signals.push_back( name );
}
}
if( ( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS )
&& ( simType == ST_TRANSIENT || simType == ST_DC ) )
{
for( const SPICE_ITEM& item : m_circuitModel->GetItems() )
{
if( item.model->GetPinCount() >= 2 )
{
wxString name = item.model->SpiceGenerator().ItemName( item );
m_signals.push_back( wxString::Format( wxS( "P(%s)" ), name ) );
}
}
}
// Add .PROBE directives
for( const wxString& directive : m_circuitModel->GetDirectives() )
{
wxString directiveParams;
if( directive.Upper().StartsWith( wxS( ".PROBE" ), &directiveParams ) )
m_signals.push_back( directiveParams.Trim( false ) );
}
rebuildSignalsGrid( m_filter->GetValue() );
applyTuners(); applyTuners();
@ -862,7 +875,7 @@ SIM_PANEL_BASE* SIM_PLOT_FRAME::NewPlotPanel( const wxString& aSimCommand, int a
if( SIM_PANEL_BASE::IsPlottable( simType ) ) if( SIM_PANEL_BASE::IsPlottable( simType ) )
{ {
SIM_PLOT_PANEL* panel = new SIM_PLOT_PANEL( aSimCommand, aOptions, m_workbook, wxID_ANY ); SIM_PLOT_PANEL* panel = new SIM_PLOT_PANEL( aSimCommand, aOptions, m_plotNotebook, wxID_ANY );
plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel ); plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
COMMON_SETTINGS::INPUT cfg = Pgm().GetCommonSettings()->m_Input; COMMON_SETTINGS::INPUT cfg = Pgm().GetCommonSettings()->m_Input;
@ -870,14 +883,14 @@ SIM_PANEL_BASE* SIM_PLOT_FRAME::NewPlotPanel( const wxString& aSimCommand, int a
} }
else else
{ {
SIM_NOPLOT_PANEL* panel = new SIM_NOPLOT_PANEL( aSimCommand, aOptions, m_workbook, wxID_ANY ); SIM_NOPLOT_PANEL* panel = new SIM_NOPLOT_PANEL( aSimCommand, aOptions, m_plotNotebook, wxID_ANY );
plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel ); plotPanel = dynamic_cast<SIM_PANEL_BASE*>( panel );
} }
wxString pageTitle( m_simulator->TypeToName( simType, true ) ); wxString pageTitle( m_simulator->TypeToName( simType, true ) );
pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), (unsigned int) ++m_plotNumber ) ); pageTitle.Prepend( wxString::Format( _( "Plot%u - " ), (unsigned int) ++m_plotNumber ) );
m_workbook->AddPage( dynamic_cast<wxWindow*>( plotPanel ), pageTitle, true ); m_plotNotebook->AddPage( dynamic_cast<wxWindow*>( plotPanel ), pageTitle, true );
return plotPanel; return plotPanel;
} }
@ -955,13 +968,10 @@ void SIM_PLOT_FRAME::onSignalsGridCellChanged( wxGridEvent& aEvent )
} }
if( traceType != SPT_UNKNOWN ) if( traceType != SPT_UNKNOWN )
{
addTrace( baseSignal, (SIM_TRACE_TYPE) traceType ); addTrace( baseSignal, (SIM_TRACE_TYPE) traceType );
} m_workbookModified = true;
}
if( !GetCurrentPlot()->GetTrace( signalName ) )
{
aEvent.Veto();
return;
} }
} }
else else
@ -983,6 +993,7 @@ void SIM_PLOT_FRAME::onSignalsGridCellChanged( wxGridEvent& aEvent )
trace->SetTraceColour( color.ToColour() ); trace->SetTraceColour( color.ToColour() );
plot->UpdateTraceStyle( trace ); plot->UpdateTraceStyle( trace );
plot->UpdatePlotColors(); plot->UpdatePlotColors();
m_workbookModified = true;
} }
} }
else if( col == COL_CURSOR_1 || col == COL_CURSOR_2 ) else if( col == COL_CURSOR_1 || col == COL_CURSOR_2 )
@ -993,6 +1004,7 @@ void SIM_PLOT_FRAME::onSignalsGridCellChanged( wxGridEvent& aEvent )
bool enable = ii == row && text == wxS( "1" ); bool enable = ii == row && text == wxS( "1" );
plot->EnableCursor( signalName, col == COL_CURSOR_1 ? 1 : 2, enable ); plot->EnableCursor( signalName, col == COL_CURSOR_1 ? 1 : 2, enable );
m_workbookModified = true;
} }
// Update cursor checkboxes (which are really radio buttons) // Update cursor checkboxes (which are really radio buttons)
@ -1054,6 +1066,8 @@ void SIM_PLOT_FRAME::DeleteMeasurement( int aRow )
m_measurementFormats[ aRow ] = m_measurementFormats[ aRow + 1 ]; m_measurementFormats[ aRow ] = m_measurementFormats[ aRow + 1 ];
m_measurementFormats.pop_back(); m_measurementFormats.pop_back();
m_workbookModified = true;
} }
@ -1090,6 +1104,8 @@ void SIM_PLOT_FRAME::updateMeasurement( int aRow )
" +" " +"
"([a-zA-Z])\\([a-zA-Z0-9_]+\\)" ) ); "([a-zA-Z])\\([a-zA-Z0-9_]+\\)" ) );
m_workbookModified = true;
SIM_PLOT_PANEL* plotPanel = GetCurrentPlot(); SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
if( !plotPanel ) if( !plotPanel )
@ -1140,12 +1156,14 @@ void SIM_PLOT_FRAME::updateMeasurement( int aRow )
void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName ) void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName )
{ {
addTrace( aNetName, SPT_VOLTAGE ); addTrace( aNetName, SPT_VOLTAGE );
m_workbookModified = true;
} }
void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName ) void SIM_PLOT_FRAME::AddCurrentPlot( const wxString& aDeviceName )
{ {
addTrace( aDeviceName, SPT_CURRENT ); addTrace( aDeviceName, SPT_CURRENT );
m_workbookModified = true;
} }
@ -1177,6 +1195,7 @@ void SIM_PLOT_FRAME::AddTuner( const SCH_SHEET_PATH& aSheetPath, SCH_SYMBOL* aSy
m_sizerTuners->Add( tuner ); m_sizerTuners->Add( tuner );
m_tuners.push_back( tuner ); m_tuners.push_back( tuner );
m_panelTuners->Layout(); m_panelTuners->Layout();
m_workbookModified = true;
} }
catch( const KI_PARAM_ERROR& e ) catch( const KI_PARAM_ERROR& e )
{ {
@ -1225,6 +1244,7 @@ void SIM_PLOT_FRAME::RemoveTuner( TUNER_SLIDER* aTuner, bool aErase )
aTuner->Destroy(); aTuner->Destroy();
m_panelTuners->Layout(); m_panelTuners->Layout();
m_workbookModified = true;
} }
@ -1244,6 +1264,7 @@ void SIM_PLOT_FRAME::AddMeasurement( const wxString& aCmd, const wxString& aSign
m_measurementsGrid->SetCellValue( row, COL_MEASUREMENT, aCmd + wxS( " " ) + aSignal ); m_measurementsGrid->SetCellValue( row, COL_MEASUREMENT, aCmd + wxS( " " ) + aSignal );
updateMeasurement( row ); updateMeasurement( row );
m_workbookModified = true;
} }
@ -1264,60 +1285,45 @@ const NGSPICE_CIRCUIT_MODEL* SIM_PLOT_FRAME::GetExporter() const
void SIM_PLOT_FRAME::addTrace( const wxString& aName, SIM_TRACE_TYPE aType ) void SIM_PLOT_FRAME::addTrace( const wxString& aName, SIM_TRACE_TYPE aType )
{ {
SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
if( !plotPanel )
{
m_simConsole->AppendText( _( "Error: no current simulation.\n" ) );
m_simConsole->SetInsertionPointEnd();
return;
}
SIM_TYPE simType = m_circuitModel->GetSimType(); SIM_TYPE simType = m_circuitModel->GetSimType();
if( simType == ST_UNKNOWN ) if( simType == ST_UNKNOWN )
{ {
m_simConsole->AppendText( _( "Error: simulation type not defined!\n" ) ); m_simConsole->AppendText( _( "Error: simulation type not defined.\n" ) );
m_simConsole->SetInsertionPointEnd(); m_simConsole->SetInsertionPointEnd();
return; return;
} }
else if( !SIM_PANEL_BASE::IsPlottable( simType ) ) else if( !SIM_PANEL_BASE::IsPlottable( simType ) )
{ {
m_simConsole->AppendText( _( "Error: simulation type doesn't support plotting!\n" ) ); m_simConsole->AppendText( _( "Error: simulation type doesn't support plotting.\n" ) );
m_simConsole->SetInsertionPointEnd(); m_simConsole->SetInsertionPointEnd();
return; return;
} }
// Create a new plot if the current one displays a different type
SIM_PLOT_PANEL* plotPanel = GetCurrentPlot();
if( !plotPanel || plotPanel->GetType() != simType )
{
plotPanel = dynamic_cast<SIM_PLOT_PANEL*>( NewPlotPanel( m_circuitModel->GetSimCommand(),
m_circuitModel->GetSimOptions() ) );
}
wxASSERT( plotPanel );
if( !plotPanel ) // Something is wrong
return;
bool updated = false;
SIM_TRACE_TYPE xAxisType = getXAxisType( simType ); SIM_TRACE_TYPE xAxisType = getXAxisType( simType );
if( xAxisType == SPT_LIN_FREQUENCY || xAxisType == SPT_LOG_FREQUENCY ) if( ( xAxisType == SPT_LIN_FREQUENCY || xAxisType == SPT_LOG_FREQUENCY )
&& ( aType & ( SPT_AC_MAG | SPT_AC_PHASE ) ) == 0 )
{ {
int baseType = aType & ~( SPT_AC_MAG | SPT_AC_PHASE );
// If magnitude or phase wasn't specified, then add both // If magnitude or phase wasn't specified, then add both
if( baseType == aType ) updateTrace( aName, (SIM_TRACE_TYPE) ( aType | SPT_AC_MAG ), plotPanel );
{ updateTrace( aName, (SIM_TRACE_TYPE) ( aType | SPT_AC_PHASE ), plotPanel );
updated |= updateTrace( aName, (SIM_TRACE_TYPE) ( baseType | SPT_AC_MAG ), plotPanel );
updated |= updateTrace( aName, (SIM_TRACE_TYPE) ( baseType | SPT_AC_PHASE ), plotPanel );
}
else
{
updated |= updateTrace( aName, (SIM_TRACE_TYPE) ( aType ), plotPanel );
}
} }
else else
{ {
updated = updateTrace( aName, aType, plotPanel ); updateTrace( aName, aType, plotPanel );
} }
if( updated ) updateSignalsGrid();
updateSignalsGrid();
} }
@ -1329,7 +1335,10 @@ void SIM_PLOT_FRAME::removeTrace( const wxString& aSignalName )
return; return;
wxASSERT( plotPanel->TraceShown( aSignalName ) ); wxASSERT( plotPanel->TraceShown( aSignalName ) );
m_workbook->DeleteTrace( plotPanel, aSignalName );
if( plotPanel->DeleteTrace( aSignalName ) )
m_workbookModified = true;
plotPanel->GetPlotWin()->Fit(); plotPanel->GetPlotWin()->Fit();
updateSignalsGrid(); updateSignalsGrid();
@ -1338,7 +1347,7 @@ void SIM_PLOT_FRAME::removeTrace( const wxString& aSignalName )
} }
bool SIM_PLOT_FRAME::updateTrace( const wxString& aName, SIM_TRACE_TYPE aType, void SIM_PLOT_FRAME::updateTrace( const wxString& aName, SIM_TRACE_TYPE aType,
SIM_PLOT_PANEL* aPlotPanel ) SIM_PLOT_PANEL* aPlotPanel )
{ {
SIM_TYPE simType = m_circuitModel->GetSimType(); SIM_TYPE simType = m_circuitModel->GetSimType();
@ -1359,14 +1368,14 @@ bool SIM_PLOT_FRAME::updateTrace( const wxString& aName, SIM_TRACE_TYPE aType,
// There is no plot to be shown // There is no plot to be shown
m_simulator->Command( wxString::Format( wxT( "print %s" ), aName ).ToStdString() ); m_simulator->Command( wxString::Format( wxT( "print %s" ), aName ).ToStdString() );
return false; return;
} }
// First, handle the x axis // First, handle the x axis
wxString xAxisName( m_simulator->GetXAxis( simType ) ); wxString xAxisName( m_simulator->GetXAxis( simType ) );
if( xAxisName.IsEmpty() ) if( xAxisName.IsEmpty() )
return false; return;
std::vector<double> data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() ); std::vector<double> data_x = m_simulator->GetMagPlot( (const char*) xAxisName.c_str() );
unsigned int size = data_x.size(); unsigned int size = data_x.size();
@ -1399,56 +1408,53 @@ bool SIM_PLOT_FRAME::updateTrace( const wxString& aName, SIM_TRACE_TYPE aType,
wxFAIL_MSG( wxT( "Unhandled plot type" ) ); wxFAIL_MSG( wxT( "Unhandled plot type" ) );
} }
if( data_y.size() == 0 )
return false; // Signal no longer exists
else
wxCHECK_MSG( data_y.size() >= size, false, wxT( "Not enough y data values to plot" ) );
// If we did a two-source DC analysis, we need to split the resulting vector and add traces // If we did a two-source DC analysis, we need to split the resulting vector and add traces
// for each input step // for each input step
SPICE_DC_PARAMS source1, source2; SPICE_DC_PARAMS source1, source2;
if( m_circuitModel->GetSimType() == ST_DC if( m_circuitModel->GetSimType() == ST_DC
&& m_circuitModel->ParseDCCommand( m_circuitModel->GetSimCommand(), &source1, &source2 ) ) && m_circuitModel->ParseDCCommand( m_circuitModel->GetSimCommand(), &source1, &source2 )
&& !source2.m_source.IsEmpty() )
{ {
if( !source2.m_source.IsEmpty() ) // Source 1 is the inner loop, so lets add traces for each Source 2 (outer loop) step
SPICE_VALUE v = source2.m_vstart;
wxString name;
size_t offset = 0;
size_t outer = ( size_t )( ( source2.m_vend - v ) / source2.m_vincrement ).ToDouble();
size_t inner = data_x.size() / ( outer + 1 );
wxASSERT( data_x.size() % ( outer + 1 ) == 0 );
for( size_t idx = 0; idx <= outer; idx++ )
{ {
// Source 1 is the inner loop, so lets add traces for each Source 2 (outer loop) step name = wxString::Format( wxT( "%s (%s = %s V)" ),
SPICE_VALUE v = source2.m_vstart; traceTitle,
wxString name; source2.m_source,
v.ToString() );
size_t offset = 0; if( TRACE* trace = aPlotPanel->AddTrace( name, aName, aType ) )
size_t outer = ( size_t )( ( source2.m_vend - v ) / source2.m_vincrement ).ToDouble();
size_t inner = data_x.size() / ( outer + 1 );
wxASSERT( data_x.size() % ( outer + 1 ) == 0 );
for( size_t idx = 0; idx <= outer; idx++ )
{ {
name = wxString::Format( wxT( "%s (%s = %s V)" ), if( data_y.size() >= size )
traceTitle, {
source2.m_source, std::vector<double> sub_x( data_x.begin() + offset,
v.ToString() ); data_x.begin() + offset + inner );
std::vector<double> sub_y( data_y.begin() + offset,
data_y.begin() + offset + inner );
std::vector<double> sub_x( data_x.begin() + offset, aPlotPanel->SetTraceData( trace, inner, sub_x.data(), sub_y.data() );
data_x.begin() + offset + inner ); }
std::vector<double> sub_y( data_y.begin() + offset,
data_y.begin() + offset + inner );
m_workbook->AddTrace( aPlotPanel, name, aName, inner, sub_x.data(), sub_y.data(),
aType );
v = v + source2.m_vincrement;
offset += inner;
} }
return true; v = v + source2.m_vincrement;
offset += inner;
} }
} }
else if( TRACE* trace = aPlotPanel->AddTrace( traceTitle, aName, aType ) )
m_workbook->AddTrace( aPlotPanel, traceTitle, aName, size, data_x.data(), data_y.data(), aType ); {
if( data_y.size() >= size )
return true; aPlotPanel->SetTraceData( trace, size, data_x.data(), data_y.data() );
}
} }
@ -1557,7 +1563,7 @@ void SIM_PLOT_FRAME::applyTuners()
bool SIM_PLOT_FRAME::LoadWorkbook( const wxString& aPath ) bool SIM_PLOT_FRAME::LoadWorkbook( const wxString& aPath )
{ {
m_workbook->DeleteAllPages(); m_plotNotebook->DeleteAllPages();
wxTextFile file( aPath ); wxTextFile file( aPath );
@ -1643,14 +1649,6 @@ bool SIM_PLOT_FRAME::LoadWorkbook( const wxString& aPath )
} }
NewPlotPanel( simCommand, simOptions ); NewPlotPanel( simCommand, simOptions );
StartSimulation();
// Perform simulation, so plots can be added with values
do
{
wxThread::This()->Sleep( 50 );
}
while( m_simulator->IsRunning() );
if( !file.GetNextLine().ToLong( &tracesCount ) ) if( !file.GetNextLine().ToLong( &tracesCount ) )
{ {
@ -1774,7 +1772,12 @@ bool SIM_PLOT_FRAME::LoadWorkbook( const wxString& aPath )
} }
} }
LoadSimulator();
rebuildSignalsList();
rebuildSignalsGrid( m_filter->GetValue() );
updateSignalsGrid(); updateSignalsGrid();
wxCommandEvent dummy; wxCommandEvent dummy;
onCursorUpdate( dummy ); onCursorUpdate( dummy );
@ -1787,7 +1790,9 @@ bool SIM_PLOT_FRAME::LoadWorkbook( const wxString& aPath )
m_simulator->Settings()->SetWorkbookFilename( filename.GetFullPath() ); m_simulator->Settings()->SetWorkbookFilename( filename.GetFullPath() );
// Successfully loading a workbook does not count as modifying it. // Successfully loading a workbook does not count as modifying it.
m_workbook->ClrModified(); m_workbookModified = false;
updateTitle();
return true; return true;
} }
@ -1813,11 +1818,11 @@ bool SIM_PLOT_FRAME::SaveWorkbook( const wxString& aPath )
file.AddLine( wxT( "version 4" ) ); file.AddLine( wxT( "version 4" ) );
file.AddLine( wxString::Format( wxT( "%llu" ), m_workbook->GetPageCount() ) ); file.AddLine( wxString::Format( wxT( "%llu" ), m_plotNotebook->GetPageCount() ) );
for( size_t i = 0; i < m_workbook->GetPageCount(); i++ ) for( size_t i = 0; i < m_plotNotebook->GetPageCount(); i++ )
{ {
const SIM_PANEL_BASE* basePanel = dynamic_cast<const SIM_PANEL_BASE*>( m_workbook->GetPage( i ) ); const SIM_PANEL_BASE* basePanel = dynamic_cast<const SIM_PANEL_BASE*>( m_plotNotebook->GetPage( i ) );
if( !basePanel ) if( !basePanel )
{ {
@ -1827,8 +1832,8 @@ bool SIM_PLOT_FRAME::SaveWorkbook( const wxString& aPath )
file.AddLine( wxString::Format( wxT( "%d" ), basePanel->GetType() ) ); file.AddLine( wxString::Format( wxT( "%d" ), basePanel->GetType() ) );
wxString command = m_workbook->GetSimCommand( basePanel ); wxString command = basePanel->GetSimCommand();
int options = m_workbook->GetSimOptions( basePanel ); int options = basePanel->GetSimOptions();
if( options & NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS ) if( options & NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS )
command += wxT( "\n.kicad adjustpaths" ); command += wxT( "\n.kicad adjustpaths" );
@ -1902,7 +1907,9 @@ bool SIM_PLOT_FRAME::SaveWorkbook( const wxString& aPath )
m_simulator->Settings()->SetWorkbookFilename( filename.GetFullPath() ); m_simulator->Settings()->SetWorkbookFilename( filename.GetFullPath() );
} }
m_workbook->ClrModified(); m_workbookModified = false;
updateTitle();
return res; return res;
} }
@ -1930,9 +1937,9 @@ void SIM_PLOT_FRAME::ToggleDarkModePlots()
SIM_PLOT_COLORS::FillDefaultColorList( m_darkMode ); SIM_PLOT_COLORS::FillDefaultColorList( m_darkMode );
// Now send changes to all SIM_PLOT_PANEL // Now send changes to all SIM_PLOT_PANEL
for( size_t page = 0; page < m_workbook->GetPageCount(); page++ ) for( size_t page = 0; page < m_plotNotebook->GetPageCount(); page++ )
{ {
wxWindow* curPage = m_workbook->GetPage( page ); wxWindow* curPage = m_plotNotebook->GetPage( page );
// ensure it is truly a plot panel and not the (zero plots) placeholder // ensure it is truly a plot panel and not the (zero plots) placeholder
// which is only SIM_PLOT_PANEL_BASE // which is only SIM_PLOT_PANEL_BASE
@ -1972,14 +1979,9 @@ void SIM_PLOT_FRAME::onPlotDragged( wxAuiNotebookEvent& event )
} }
void SIM_PLOT_FRAME::onWorkbookModified( wxCommandEvent& event ) void SIM_PLOT_FRAME::onNotebookModified( wxCommandEvent& event )
{
updateTitle();
}
void SIM_PLOT_FRAME::onWorkbookClrModified( wxCommandEvent& event )
{ {
m_workbookModified = true;
updateTitle(); updateTitle();
} }
@ -1999,10 +2001,10 @@ bool SIM_PLOT_FRAME::EditSimCommand()
return false; return false;
} }
if( m_workbook->GetPageIndex( plotPanelWindow ) != wxNOT_FOUND ) if( m_plotNotebook->GetPageIndex( plotPanelWindow ) != wxNOT_FOUND )
{ {
dlg.SetSimCommand( m_workbook->GetSimCommand( plotPanelWindow ) ); dlg.SetSimCommand( plotPanelWindow->GetSimCommand() );
dlg.SetSimOptions( m_workbook->GetSimOptions( plotPanelWindow ) ); dlg.SetSimOptions( plotPanelWindow->GetSimOptions() );
} }
else else
{ {
@ -2013,8 +2015,8 @@ bool SIM_PLOT_FRAME::EditSimCommand()
{ {
wxString oldCommand; wxString oldCommand;
if( m_workbook->GetPageIndex( plotPanelWindow ) != wxNOT_FOUND ) if( m_plotNotebook->GetPageIndex( plotPanelWindow ) != wxNOT_FOUND )
oldCommand = m_workbook->GetSimCommand( plotPanelWindow ); oldCommand = plotPanelWindow->GetSimCommand();
else else
oldCommand = wxString(); oldCommand = wxString();
@ -2038,14 +2040,15 @@ bool SIM_PLOT_FRAME::EditSimCommand()
} }
else else
{ {
if( m_workbook->GetPageIndex( plotPanelWindow ) == 0 ) if( m_plotNotebook->GetPageIndex( plotPanelWindow ) == 0 )
m_circuitModel->SetSimCommandOverride( newCommand ); m_circuitModel->SetSimCommandOverride( newCommand );
// Update simulation command in the current plot // Update simulation command in the current plot
m_workbook->SetSimCommand( plotPanelWindow, newCommand ); plotPanelWindow->SetSimCommand( newCommand );
m_workbook->SetSimOptions( plotPanelWindow, newOptions ); plotPanelWindow->SetSimOptions( newOptions );
} }
m_workbookModified = true;
m_simulator->Init(); m_simulator->Init();
return true; return true;
} }
@ -2056,7 +2059,7 @@ bool SIM_PLOT_FRAME::EditSimCommand()
bool SIM_PLOT_FRAME::canCloseWindow( wxCloseEvent& aEvent ) bool SIM_PLOT_FRAME::canCloseWindow( wxCloseEvent& aEvent )
{ {
if( m_workbook->IsModified() ) if( m_workbookModified )
{ {
wxFileName filename = m_simulator->Settings()->GetWorkbookFilename(); wxFileName filename = m_simulator->Settings()->GetWorkbookFilename();
@ -2372,10 +2375,7 @@ void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent )
} }
for( const struct TRACE_DESC& trace : traceInfo ) for( const struct TRACE_DESC& trace : traceInfo )
{ updateTrace( trace.m_name, trace.m_type, plotPanel );
if( !updateTrace( trace.m_name, trace.m_type, plotPanel ) )
removeTrace( trace.m_name );
}
rebuildSignalsGrid( m_filter->GetValue() ); rebuildSignalsGrid( m_filter->GetValue() );
plotPanel->GetPlotWin()->UpdateAll(); plotPanel->GetPlotWin()->UpdateAll();

View File

@ -50,7 +50,7 @@ class NGSPICE_CIRCUIT_MODEL;
#include <sim/sim_plot_panel.h> #include <sim/sim_plot_panel.h>
#include <sim/sim_panel_base.h> #include <sim/sim_panel_base.h>
#include <sim/sim_workbook.h> #include <sim/sim_notebook.h>
class SIM_THREAD_REPORTER; class SIM_THREAD_REPORTER;
class TUNER_SLIDER; class TUNER_SLIDER;
@ -62,6 +62,12 @@ public:
SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ); SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent );
~SIM_PLOT_FRAME(); ~SIM_PLOT_FRAME();
/**
* Check and load the current netlist into the simulator.
* @return true if document is fully annotated and netlist was loaded successfully.
*/
bool LoadSimulator();
void StartSimulation(); void StartSimulation();
/** /**
@ -208,18 +214,18 @@ public:
wxString GetCurrentSimCommand() const wxString GetCurrentSimCommand() const
{ {
if( getCurrentPlotWindow() == nullptr ) if( getCurrentPlotWindow() )
return m_circuitModel->GetSchTextSimCommand(); return getCurrentPlotWindow()->GetSimCommand();
else else
return m_workbook->GetSimCommand( getCurrentPlotWindow() ); return m_circuitModel->GetSchTextSimCommand();
} }
int GetCurrentOptions() const int GetCurrentOptions() const
{ {
if( getCurrentPlotWindow() == nullptr ) if( getCurrentPlotWindow() )
return m_circuitModel->GetSimOptions(); return getCurrentPlotWindow()->GetSimOptions();
else else
return m_workbook->GetSimOptions( getCurrentPlotWindow() ); return m_circuitModel->GetSimOptions();
} }
// Simulator doesn't host a tool framework // Simulator doesn't host a tool framework
@ -266,9 +272,15 @@ private:
* @param aType describes the type of plot. * @param aType describes the type of plot.
* @param aParam is the parameter for the device/net (e.g. I, Id, V). * @param aParam is the parameter for the device/net (e.g. I, Id, V).
* @param aPlotPanel is the panel that should receive the update. * @param aPlotPanel is the panel that should receive the update.
* @return True if a plot was successfully added/updated.
*/ */
bool updateTrace( const wxString& aName, SIM_TRACE_TYPE aType, SIM_PLOT_PANEL* aPlotPanel ); void updateTrace( const wxString& aName, SIM_TRACE_TYPE aType, SIM_PLOT_PANEL* aPlotPanel );
/**
* Rebuild the list of signals available from the netlist.
*
* Note: this is not the filtered list. See rebuildSignalsGrid() for that.
*/
void rebuildSignalsList();
/** /**
* Rebuild the filtered list of signals in the signals grid. * Rebuild the filtered list of signals in the signals grid.
@ -295,7 +307,7 @@ private:
*/ */
SIM_PANEL_BASE* getCurrentPlotWindow() const SIM_PANEL_BASE* getCurrentPlotWindow() const
{ {
return dynamic_cast<SIM_PANEL_BASE*>( m_workbook->GetCurrentPage() ); return dynamic_cast<SIM_PANEL_BASE*>( m_plotNotebook->GetCurrentPage() );
} }
/** /**
@ -319,8 +331,7 @@ private:
void onCursorsGridCellChanged( wxGridEvent& aEvent ) override; void onCursorsGridCellChanged( wxGridEvent& aEvent ) override;
void onMeasurementsGridCellChanged( wxGridEvent& aEvent ) override; void onMeasurementsGridCellChanged( wxGridEvent& aEvent ) override;
void onWorkbookModified( wxCommandEvent& event ); void onNotebookModified( wxCommandEvent& event );
void onWorkbookClrModified( wxCommandEvent& event );
bool canCloseWindow( wxCloseEvent& aEvent ) override; bool canCloseWindow( wxCloseEvent& aEvent ) override;
void doCloseWindow() override; void doCloseWindow() override;
@ -364,6 +375,7 @@ private:
unsigned int m_plotNumber; unsigned int m_plotNumber;
bool m_simFinished; bool m_simFinished;
unsigned int m_outputCounter; unsigned int m_outputCounter;
bool m_workbookModified;
}; };
// Commands // Commands

View File

@ -43,11 +43,11 @@ SIM_PLOT_FRAME_BASE::SIM_PLOT_FRAME_BASE( wxWindow* parent, wxWindowID id, const
m_sizerPlot = new wxBoxSizer( wxHORIZONTAL ); m_sizerPlot = new wxBoxSizer( wxHORIZONTAL );
m_workbook = new SIM_WORKBOOK( m_plotPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_NB_CLOSE_ON_ALL_TABS|wxAUI_NB_MIDDLE_CLICK_CLOSE|wxAUI_NB_TAB_MOVE|wxAUI_NB_TOP ); m_plotNotebook = new SIM_NOTEBOOK( m_plotPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxAUI_NB_CLOSE_ON_ALL_TABS|wxAUI_NB_MIDDLE_CLICK_CLOSE|wxAUI_NB_TAB_MOVE|wxAUI_NB_TOP );
m_workbook->SetMinSize( wxSize( 200,-1 ) ); m_plotNotebook->SetMinSize( wxSize( 200,-1 ) );
m_sizerPlot->Add( m_workbook, 1, wxEXPAND, 5 ); m_sizerPlot->Add( m_plotNotebook, 1, wxEXPAND, 5 );
m_plotPanel->SetSizer( m_sizerPlot ); m_plotPanel->SetSizer( m_sizerPlot );
@ -284,10 +284,10 @@ SIM_PLOT_FRAME_BASE::SIM_PLOT_FRAME_BASE( wxWindow* parent, wxWindowID id, const
this->Centre( wxBOTH ); this->Centre( wxBOTH );
// Connect Events // Connect Events
m_workbook->Connect( wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotDragged ), NULL, this ); m_plotNotebook->Connect( wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotDragged ), NULL, this );
m_workbook->Connect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotChanged ), NULL, this ); m_plotNotebook->Connect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotChanged ), NULL, this );
m_workbook->Connect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotClose ), NULL, this ); m_plotNotebook->Connect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotClose ), NULL, this );
m_workbook->Connect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotClosed ), NULL, this ); m_plotNotebook->Connect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotClosed ), NULL, this );
m_filter->Connect( wxEVT_MOTION, wxMouseEventHandler( SIM_PLOT_FRAME_BASE::OnFilterMouseMoved ), NULL, this ); m_filter->Connect( wxEVT_MOTION, wxMouseEventHandler( SIM_PLOT_FRAME_BASE::OnFilterMouseMoved ), NULL, this );
m_filter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::OnFilterText ), NULL, this ); m_filter->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::OnFilterText ), NULL, this );
m_signalsGrid->Connect( wxEVT_GRID_CELL_CHANGED, wxGridEventHandler( SIM_PLOT_FRAME_BASE::onSignalsGridCellChanged ), NULL, this ); m_signalsGrid->Connect( wxEVT_GRID_CELL_CHANGED, wxGridEventHandler( SIM_PLOT_FRAME_BASE::onSignalsGridCellChanged ), NULL, this );
@ -298,10 +298,10 @@ SIM_PLOT_FRAME_BASE::SIM_PLOT_FRAME_BASE( wxWindow* parent, wxWindowID id, const
SIM_PLOT_FRAME_BASE::~SIM_PLOT_FRAME_BASE() SIM_PLOT_FRAME_BASE::~SIM_PLOT_FRAME_BASE()
{ {
// Disconnect Events // Disconnect Events
m_workbook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotDragged ), NULL, this ); m_plotNotebook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotDragged ), NULL, this );
m_workbook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotChanged ), NULL, this ); m_plotNotebook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotChanged ), NULL, this );
m_workbook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotClose ), NULL, this ); m_plotNotebook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotClose ), NULL, this );
m_workbook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotClosed ), NULL, this ); m_plotNotebook->Disconnect( wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, wxAuiNotebookEventHandler( SIM_PLOT_FRAME_BASE::onPlotClosed ), NULL, this );
m_filter->Disconnect( wxEVT_MOTION, wxMouseEventHandler( SIM_PLOT_FRAME_BASE::OnFilterMouseMoved ), NULL, this ); m_filter->Disconnect( wxEVT_MOTION, wxMouseEventHandler( SIM_PLOT_FRAME_BASE::OnFilterMouseMoved ), NULL, this );
m_filter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::OnFilterText ), NULL, this ); m_filter->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( SIM_PLOT_FRAME_BASE::OnFilterText ), NULL, this );
m_signalsGrid->Disconnect( wxEVT_GRID_CELL_CHANGED, wxGridEventHandler( SIM_PLOT_FRAME_BASE::onSignalsGridCellChanged ), NULL, this ); m_signalsGrid->Disconnect( wxEVT_GRID_CELL_CHANGED, wxGridEventHandler( SIM_PLOT_FRAME_BASE::onSignalsGridCellChanged ), NULL, this );

View File

@ -307,7 +307,7 @@
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style"></property> <property name="window_style"></property>
<object class="splitteritem" expanded="1"> <object class="splitteritem" expanded="1">
<object class="wxPanel" expanded="0"> <object class="wxPanel" expanded="1">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -358,7 +358,7 @@
<property name="window_extra_style"></property> <property name="window_extra_style"></property>
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style">wxTAB_TRAVERSAL</property> <property name="window_style">wxTAB_TRAVERSAL</property>
<object class="wxBoxSizer" expanded="0"> <object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="name">m_sizerPlot</property> <property name="name">m_sizerPlot</property>
<property name="orient">wxHORIZONTAL</property> <property name="orient">wxHORIZONTAL</property>
@ -402,7 +402,7 @@
<property name="minimize_button">0</property> <property name="minimize_button">0</property>
<property name="minimum_size">200,-1</property> <property name="minimum_size">200,-1</property>
<property name="moveable">1</property> <property name="moveable">1</property>
<property name="name">m_workbook</property> <property name="name">m_plotNotebook</property>
<property name="pane_border">1</property> <property name="pane_border">1</property>
<property name="pane_position"></property> <property name="pane_position"></property>
<property name="pane_size"></property> <property name="pane_size"></property>
@ -413,7 +413,7 @@
<property name="show">1</property> <property name="show">1</property>
<property name="size"></property> <property name="size"></property>
<property name="style">wxAUI_NB_CLOSE_ON_ALL_TABS|wxAUI_NB_MIDDLE_CLICK_CLOSE|wxAUI_NB_TAB_MOVE|wxAUI_NB_TOP</property> <property name="style">wxAUI_NB_CLOSE_ON_ALL_TABS|wxAUI_NB_MIDDLE_CLICK_CLOSE|wxAUI_NB_TAB_MOVE|wxAUI_NB_TOP</property>
<property name="subclass">SIM_WORKBOOK; sim_workbook.h; Not forward_declare</property> <property name="subclass">SIM_NOTEBOOK; sim_notebook.h; </property>
<property name="tab_ctrl_height">-1</property> <property name="tab_ctrl_height">-1</property>
<property name="toolbar_pane">0</property> <property name="toolbar_pane">0</property>
<property name="tooltip"></property> <property name="tooltip"></property>

View File

@ -13,7 +13,7 @@
class ACTION_TOOLBAR; class ACTION_TOOLBAR;
class WX_GRID; class WX_GRID;
#include "sim_workbook.h" #include "sim_notebook.h"
#include "kiway_player.h" #include "kiway_player.h"
#include <wx/gdicmn.h> #include <wx/gdicmn.h>
#include <wx/aui/aui.h> #include <wx/aui/aui.h>
@ -50,7 +50,7 @@ class SIM_PLOT_FRAME_BASE : public KIWAY_PLAYER
wxSplitterWindow* m_splitterPlotAndConsole; wxSplitterWindow* m_splitterPlotAndConsole;
wxPanel* m_plotPanel; wxPanel* m_plotPanel;
wxBoxSizer* m_sizerPlot; wxBoxSizer* m_sizerPlot;
SIM_WORKBOOK* m_workbook; SIM_NOTEBOOK* m_plotNotebook;
wxPanel* m_panelConsole; wxPanel* m_panelConsole;
wxBoxSizer* m_sizerConsole; wxBoxSizer* m_sizerConsole;
wxTextCtrl* m_simConsole; wxTextCtrl* m_simConsole;

View File

@ -551,7 +551,7 @@ void SIM_PLOT_PANEL::updateAxes( SIM_TRACE_TYPE aNewTraceType )
void SIM_PLOT_PANEL::prepareDCAxes() void SIM_PLOT_PANEL::prepareDCAxes()
{ {
wxString sim_cmd = getSimCommand().Lower(); wxString sim_cmd = GetSimCommand().Lower();
wxString rem; wxString rem;
if( sim_cmd.StartsWith( ".dc", &rem ) ) if( sim_cmd.StartsWith( ".dc", &rem ) )
@ -676,67 +676,67 @@ void SIM_PLOT_PANEL::UpdateTraceStyle( TRACE* trace )
} }
bool SIM_PLOT_PANEL::addTrace( const wxString& aTitle, const wxString& aName, int aPoints, TRACE* SIM_PLOT_PANEL::AddTrace( const wxString& aTitle, const wxString& aName,
const double* aX, const double* aY, SIM_TRACE_TYPE aType ) SIM_TRACE_TYPE aType )
{ {
TRACE* trace = nullptr; TRACE* trace = nullptr;
auto it = m_traces.find( aTitle );
if( it != m_traces.end() )
return it->second;
updateAxes( aType ); updateAxes( aType );
// Find previous entry, if there is one if( GetType() == ST_TRANSIENT )
auto prev = m_traces.find( aTitle );
bool addedNewEntry = ( prev == m_traces.end() );
if( addedNewEntry )
{ {
if( GetType() == ST_TRANSIENT ) bool hasVoltageTraces = false;
for( const auto& [ name, candidate ] : m_traces )
{ {
bool hasVoltageTraces = false; if( candidate->GetType() & SPT_VOLTAGE )
for( const auto& [ name, candidate ] : m_traces )
{ {
if( candidate->GetType() & SPT_VOLTAGE ) hasVoltageTraces = true;
{ break;
hasVoltageTraces = true;
break;
}
}
if( !hasVoltageTraces )
{
if( m_axis_y2 )
m_axis_y2->SetMasterScale( nullptr );
if( m_axis_y3 )
m_axis_y3->SetMasterScale( nullptr );
} }
} }
// New entry if( !hasVoltageTraces )
trace = new TRACE( aName, aType ); {
trace->SetTraceColour( m_colors.GenerateColor( m_traces ) ); if( m_axis_y2 )
UpdateTraceStyle( trace ); m_axis_y2->SetMasterScale( nullptr );
m_traces[ aTitle ] = trace;
m_plotWin->AddLayer( (mpLayer*) trace ); if( m_axis_y3 )
} m_axis_y3->SetMasterScale( nullptr );
else }
{
trace = prev->second;
} }
trace = new TRACE( aName, aType );
trace->SetTraceColour( m_colors.GenerateColor( m_traces ) );
UpdateTraceStyle( trace );
m_traces[ aTitle ] = trace;
m_plotWin->AddLayer( (mpLayer*) trace );
return trace;
}
void SIM_PLOT_PANEL::SetTraceData( TRACE* trace, unsigned int aPoints, const double* aX,
const double* aY )
{
std::vector<double> tmp( aY, aY + aPoints ); std::vector<double> tmp( aY, aY + aPoints );
if( GetType() == ST_AC ) if( GetType() == ST_AC )
{ {
if( aType & SPT_AC_PHASE ) if( trace->GetType() & SPT_AC_PHASE )
{ {
for( int i = 0; i < aPoints; i++ ) for( unsigned int i = 0; i < aPoints; i++ )
tmp[i] = tmp[i] * 180.0 / M_PI; // convert to degrees tmp[i] = tmp[i] * 180.0 / M_PI; // convert to degrees
} }
else else
{ {
for( int i = 0; i < aPoints; i++ ) for( unsigned int i = 0; i < aPoints; i++ )
{ {
// log( 0 ) is not valid. // log( 0 ) is not valid.
if( tmp[i] != 0 ) if( tmp[i] != 0 )
@ -747,20 +747,18 @@ bool SIM_PLOT_PANEL::addTrace( const wxString& aTitle, const wxString& aName, in
trace->SetData( std::vector<double>( aX, aX + aPoints ), tmp ); trace->SetData( std::vector<double>( aX, aX + aPoints ), tmp );
if( ( aType & SPT_AC_PHASE ) || ( aType & SPT_CURRENT ) ) if( ( trace->GetType() & SPT_AC_PHASE ) || ( trace->GetType() & SPT_CURRENT ) )
trace->SetScale( m_axis_x, m_axis_y2 ); trace->SetScale( m_axis_x, m_axis_y2 );
else if( aType & SPT_POWER ) else if( trace->GetType() & SPT_POWER )
trace->SetScale( m_axis_x, m_axis_y3 ); trace->SetScale( m_axis_x, m_axis_y3 );
else else
trace->SetScale( m_axis_x, m_axis_y1 ); trace->SetScale( m_axis_x, m_axis_y1 );
m_plotWin->UpdateAll(); m_plotWin->UpdateAll();
return addedNewEntry;
} }
bool SIM_PLOT_PANEL::deleteTrace( const wxString& aName ) bool SIM_PLOT_PANEL::DeleteTrace( const wxString& aName )
{ {
auto it = m_traces.find( aName ); auto it = m_traces.find( aName );

View File

@ -185,8 +185,6 @@ protected:
class SIM_PLOT_PANEL : public SIM_PANEL_BASE class SIM_PLOT_PANEL : public SIM_PANEL_BASE
{ {
friend class SIM_WORKBOOK;
public: public:
SIM_PLOT_PANEL( const wxString& aCommand, int aOptions, wxWindow* parent, wxWindowID id, SIM_PLOT_PANEL( const wxString& aCommand, int aOptions, wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
@ -303,11 +301,11 @@ public:
return m_plotWin; return m_plotWin;
} }
protected: TRACE* AddTrace( const wxString& aTitle, const wxString& aName, SIM_TRACE_TYPE aType );
bool addTrace( const wxString& aTitle, const wxString& aName, int aPoints, const double* aX,
const double* aY, SIM_TRACE_TYPE aType );
bool deleteTrace( const wxString& aName ); void SetTraceData( TRACE* aTrace, unsigned int aPoints, const double* aX, const double* aY );
bool DeleteTrace( const wxString& aName );
private: private:
///< @brief Construct the plot axes for DC simulation plot. ///< @brief Construct the plot axes for DC simulation plot.