diff --git a/eeschema/sim/ngspice.cpp b/eeschema/sim/ngspice.cpp index 180873016a..a918ca737d 100644 --- a/eeschema/sim/ngspice.cpp +++ b/eeschema/sim/ngspice.cpp @@ -24,10 +24,10 @@ */ #include "ngspice.h" +#include "spice_reporter.h" #include #include -#include #include // TODO cmake modules to add include directory for ngspice @@ -46,6 +46,7 @@ NGSPICE::NGSPICE() m_ngGet_Vec_Info = (ngGet_Vec_Info) m_dll->GetSymbol( "ngGet_Vec_Info" ); m_ngSpice_AllPlots = (ngSpice_AllPlots) m_dll->GetSymbol( "ngSpice_AllPlots" ); m_ngSpice_AllVecs = (ngSpice_AllVecs) m_dll->GetSymbol( "ngSpice_AllVecs" ); + m_ngSpice_Running = (ngSpice_Running) m_dll->GetSymbol( "ngSpice_running" ); } @@ -57,7 +58,7 @@ NGSPICE::~NGSPICE() void NGSPICE::Init() { - m_ngSpice_Init( &cbSendChar, &cbSendStat, NULL, NULL, NULL, NULL, this ); + m_ngSpice_Init( &cbSendChar, &cbSendStat, NULL, NULL, NULL, &cbBGThreadRunning, this ); } @@ -105,14 +106,25 @@ bool NGSPICE::LoadNetlist( const string& aNetlist ) bool NGSPICE::Run() { - return Command( "run\n" ); + return Command( "bg_run" ); +} + + +bool NGSPICE::Stop() +{ + return Command( "bg_halt" ); +} + + +bool NGSPICE::IsRunning() +{ + return m_ngSpice_Running(); } bool NGSPICE::Command( const string& aCmd ) { - m_ngSpice_Command( (char*)( aCmd + string( "\n" ) ).c_str() ); - dump(); + m_ngSpice_Command( (char*) aCmd.c_str() ); return true; } @@ -197,8 +209,8 @@ int NGSPICE::cbSendChar( char* what, int id, void* user ) { NGSPICE* sim = reinterpret_cast( user ); - if( sim->m_consoleReporter ) - sim->m_consoleReporter->Report( what ); + if( sim->m_reporter ) + sim->m_reporter->Report( what ); return 0; } @@ -209,5 +221,18 @@ int NGSPICE::cbSendStat( char* what, int id, void* user ) /* NGSPICE* sim = reinterpret_cast( user ); if( sim->m_consoleReporter ) sim->m_consoleReporter->Report( what );*/ + + return 0; +} + + +int NGSPICE::cbBGThreadRunning( bool is_running, int id, void* user ) +{ + NGSPICE* sim = reinterpret_cast( user ); + + if( sim->m_reporter ) + // I know the test below seems like an error, but well, it works somehow.. + sim->m_reporter->OnSimStateChange( sim, is_running ? SIM_IDLE : SIM_RUNNING ); + return 0; } diff --git a/eeschema/sim/ngspice.h b/eeschema/sim/ngspice.h index 262a8a2e98..7decad324e 100644 --- a/eeschema/sim/ngspice.h +++ b/eeschema/sim/ngspice.h @@ -39,6 +39,8 @@ public: void Init() override; bool LoadNetlist( const std::string& aNetlist ) override; bool Run() override; + bool Stop() override; + bool IsRunning() override; bool Command( const std::string& aCmd ) override; const std::vector GetPlot( const std::string& aName, int aMaxLen = -1 ) override; @@ -50,8 +52,9 @@ private: typedef int (*ngSpice_Circ)(char** circarray); typedef int (*ngSpice_Command)(char* command); typedef pvector_info (*ngGet_Vec_Info)(char* vecname); - typedef char** (*ngSpice_AllVecs)(char* plotname); typedef char** (*ngSpice_AllPlots)(void); + typedef char** (*ngSpice_AllVecs)(char* plotname); + typedef bool (*ngSpice_Running)(void); ngSpice_Init m_ngSpice_Init; ngSpice_Circ m_ngSpice_Circ; @@ -59,11 +62,13 @@ private: ngGet_Vec_Info m_ngGet_Vec_Info; ngSpice_AllPlots m_ngSpice_AllPlots; ngSpice_AllVecs m_ngSpice_AllVecs; + ngSpice_Running m_ngSpice_Running; wxDynamicLibrary* m_dll; static int cbSendChar( char* what, int id, void* user ); static int cbSendStat( char* what, int id, void* user ); + static int cbBGThreadRunning( bool is_running, int id, void* user ); void dump(); }; diff --git a/eeschema/sim/sim_plot_frame.cpp b/eeschema/sim/sim_plot_frame.cpp index c01d170767..ef6d6c5014 100644 --- a/eeschema/sim/sim_plot_frame.cpp +++ b/eeschema/sim/sim_plot_frame.cpp @@ -30,13 +30,12 @@ #include #include -#include - #include "sim_plot_frame.h" #include "sim_plot_panel.h" #include "spice_simulator.h" +#include "spice_reporter.h" -class SIM_THREAD_REPORTER : public REPORTER +class SIM_THREAD_REPORTER : public SPICE_REPORTER { public: SIM_THREAD_REPORTER( SIM_PLOT_FRAME* aParent ) @@ -44,48 +43,34 @@ public: { } - virtual REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_UNDEFINED ) + REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_UNDEFINED ) override { - wxThreadEvent* event = new wxThreadEvent( wxEVT_SIM_REPORT ); - event->SetPayload( aText ); + wxCommandEvent* event = new wxCommandEvent( wxEVT_SIM_REPORT ); + event->SetString( aText ); wxQueueEvent( m_parent, event ); return *this; } -private: - SIM_PLOT_FRAME* m_parent; -}; - - -class SIM_THREAD : public wxThread -{ -public: - SIM_THREAD( SIM_PLOT_FRAME* aParent, SPICE_SIMULATOR* aSimulator ) - : m_parent( aParent ), m_sim( aSimulator ) - {} - - ~SIM_THREAD() + void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override { - wxCriticalSectionLocker lock( m_parent->m_simThreadCS ); + wxCommandEvent* event = NULL; - // Let know the parent that the pointer is not valid anymore - m_parent->m_simThread = NULL; + switch( aNewState ) + { + case SIM_IDLE: + event = new wxCommandEvent( wxEVT_SIM_FINISHED ); + break; + + case SIM_RUNNING: + event = new wxCommandEvent( wxEVT_SIM_STARTED ); + break; + } + + wxQueueEvent( m_parent, event ); } private: - // Thread routine - ExitCode Entry() - { - assert( m_sim ); - - m_sim->Run(); - wxQueueEvent( m_parent, new wxThreadEvent( wxEVT_SIM_FINISHED ) ); - - return (ExitCode) 0; - } - SIM_PLOT_FRAME* m_parent; - SPICE_SIMULATOR* m_sim; }; @@ -96,12 +81,11 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) m_exporter = NULL; m_simulator = NULL; - m_simThread = NULL; Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( SIM_PLOT_FRAME::onClose ), NULL, this ); - Connect( wxEVT_SIM_REPORT, wxThreadEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this ); - Connect( wxEVT_SIM_FINISHED, wxThreadEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this ); - Connect( wxEVT_IDLE, wxIdleEventHandler( SIM_PLOT_FRAME::onIdle ), NULL, this ); + Connect( wxEVT_SIM_REPORT, wxCommandEventHandler( SIM_PLOT_FRAME::onSimReport ), NULL, this ); + Connect( wxEVT_SIM_STARTED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimStarted ), NULL, this ); + Connect( wxEVT_SIM_FINISHED, wxCommandEventHandler( SIM_PLOT_FRAME::onSimFinished ), NULL, this ); NewPlotPanel(); } @@ -109,9 +93,6 @@ SIM_PLOT_FRAME::SIM_PLOT_FRAME( KIWAY* aKiway, wxWindow* aParent ) SIM_PLOT_FRAME::~SIM_PLOT_FRAME() { - // m_simThread should be already destroyed by onClose() - assert( m_simThread == NULL ); - delete m_exporter; delete m_simulator; } @@ -126,7 +107,7 @@ void SIM_PLOT_FRAME::StartSimulation() /// @todo is it necessary to recreate simulator every time? m_simulator = SPICE_SIMULATOR::CreateInstance( "ngspice" ); - m_simulator->SetConsoleReporter( new SIM_THREAD_REPORTER( this ) ); + m_simulator->SetReporter( new SIM_THREAD_REPORTER( this ) ); m_simulator->Init(); NETLIST_OBJECT_LIST* net_atoms = m_schematicFrame->BuildNetListBase(); @@ -135,58 +116,13 @@ void SIM_PLOT_FRAME::StartSimulation() m_exporter->Format( &formatter, GNL_ALL ); m_simulator->LoadNetlist( formatter.GetString() ); - - // Execute the simulation in a separate thread - { - wxCriticalSectionLocker lock( m_simThreadCS ); - - assert( m_simThread == NULL ); - m_simThread = new SIM_THREAD( this, m_simulator ); - - if( m_simThread->Run() != wxTHREAD_NO_ERROR ) - { - wxLogError( "Can't create the simulator thread!" ); - delete m_simThread; - } - } -} - - -void SIM_PLOT_FRAME::PauseSimulation() -{ - wxCriticalSectionLocker lock( m_simThreadCS ); - - if( m_simThread ) - { - if( m_simThread->Pause() != wxTHREAD_NO_ERROR ) - wxLogError( "Cannot pause the simulation thread" ); - } -} - - -void SIM_PLOT_FRAME::ResumeSimulation() -{ - wxCriticalSectionLocker lock( m_simThreadCS ); - - if( m_simThread ) - { - if( m_simThread->Resume() != wxTHREAD_NO_ERROR ) - wxLogError( "Cannot resume the simulation thread" ); - } + m_simulator->Run(); } void SIM_PLOT_FRAME::StopSimulation() { - wxCriticalSectionLocker lock( m_simThreadCS ); - - if( m_simThread ) - { - // we could use m_simThread->Delete() if there was a way to run the simulation - // in parts, so the thread would be able to call TestDestroy() - if( m_simThread->Kill() != wxTHREAD_NO_ERROR ) - wxLogError( "Cannot delete the simulation thread" ); - } + m_simulator->Stop(); } @@ -212,9 +148,7 @@ void SIM_PLOT_FRAME::AddVoltagePlot( const wxString& aNetName ) bool SIM_PLOT_FRAME::isSimulationRunning() { - wxCriticalSectionLocker lock( m_simThreadCS ); - - return ( m_simThread != NULL ); + return m_simulator ? m_simulator->IsRunning() : false; } @@ -275,65 +209,29 @@ void SIM_PLOT_FRAME::onPlaceProbe( wxCommandEvent& event ) void SIM_PLOT_FRAME::onClose( wxCloseEvent& aEvent ) { - { - wxCriticalSectionLocker lock( m_simThreadCS ); - - if( m_simThread ) - { - if( m_simThread->Delete() != wxTHREAD_NO_ERROR ) - wxLogError( "Cannot delete the simulation thread" ); - } - } - - int timeout = 10; - - while( 1 ) - { - // Wait until the thread is finished - { - wxCriticalSectionLocker lock( m_simThreadCS ); - - if( m_simThread == NULL ) - break; - } - - wxThread::This()->Sleep( 1 ); - - if( --timeout == 0 ) - { - m_simThread->Kill(); // no mercy - break; - } - } + if( isSimulationRunning() ) + m_simulator->Stop(); Destroy(); } -void SIM_PLOT_FRAME::onIdle( wxIdleEvent& aEvent ) +void SIM_PLOT_FRAME::onSimStarted( wxCommandEvent& aEvent ) { - if( isSimulationRunning() ) - m_simulateBtn->SetLabel( wxT( "Stop" ) ); - else - m_simulateBtn->SetLabel( wxT( "Simulate" ) ); + m_simulateBtn->SetLabel( wxT( "Stop" ) ); + SetCursor( wxCURSOR_ARROWWAIT ); } -void SIM_PLOT_FRAME::onSimReport( wxThreadEvent& aEvent ) +void SIM_PLOT_FRAME::onSimFinished( wxCommandEvent& aEvent ) { - m_simConsole->WriteText( aEvent.GetPayload() ); - m_simConsole->Newline(); -} - - -void SIM_PLOT_FRAME::onSimFinished( wxThreadEvent& aEvent ) -{ - const auto& netMapping = m_exporter->GetNetIndexMap(); + m_simulateBtn->SetLabel( wxT( "Simulate" ) ); + SetCursor( wxCURSOR_ARROW ); // Fill the signals listbox m_signals->Clear(); - for( const auto& net : netMapping ) + for( const auto& net : m_exporter->GetNetIndexMap() ) { if( net.first != "GND" ) m_signals->Append( net.first ); @@ -343,8 +241,17 @@ void SIM_PLOT_FRAME::onSimFinished( wxThreadEvent& aEvent ) for( unsigned int i = 0; i < m_plotNotebook->GetPageCount(); ++i ) { SIM_PLOT_PANEL* plotPanel = static_cast( m_plotNotebook->GetPage( i ) ); + plotPanel->ResetAxisRanges(); for( const auto& trace : plotPanel->GetTraces() ) updatePlot( trace.spiceName, trace.title, plotPanel ); } } + + +void SIM_PLOT_FRAME::onSimReport( wxCommandEvent& aEvent ) +{ + m_simConsole->AppendText( aEvent.GetString() ); + m_simConsole->Newline(); + m_simConsole->MoveEnd(); /// @todo does not work.. +} diff --git a/eeschema/sim/sim_plot_frame.h b/eeschema/sim/sim_plot_frame.h index 6132d343e3..c688cc93dc 100644 --- a/eeschema/sim/sim_plot_frame.h +++ b/eeschema/sim/sim_plot_frame.h @@ -27,19 +27,17 @@ #define __sim_plot_frame__ /** -@file -Subclass of SIM_PLOT_FRAME_BASE, which is generated by wxFormBuilder. -*/ +@file Subclass of SIM_PLOT_FRAME_BASE, which is generated by wxFormBuilder. */ #include "sim_plot_frame_base.h" #include "kiway_player.h" #include + #include class SPICE_SIMULATOR; class NETLIST_EXPORTER_PSPICE; class SIM_PLOT_PANEL; -class SIM_THREAD; /** Implementing SIM_PLOT_FRAME_BASE */ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE @@ -55,8 +53,6 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE } void StartSimulation(); - void PauseSimulation(); - void ResumeSimulation(); void StopSimulation(); void NewPlotPanel(); @@ -92,20 +88,18 @@ class SIM_PLOT_FRAME : public SIM_PLOT_FRAME_BASE void onPlaceProbe( wxCommandEvent& event ) override; void onClose( wxCloseEvent& aEvent ); - void onIdle( wxIdleEvent& aEvent ); - void onSimReport( wxThreadEvent& aEvent ); - void onSimFinished( wxThreadEvent& aEvent ); + + void onSimStarted( wxCommandEvent& aEvent ); + void onSimFinished( wxCommandEvent& aEvent ); + void onSimReport( wxCommandEvent& aEvent ); SCH_EDIT_FRAME* m_schematicFrame; NETLIST_EXPORTER_PSPICE* m_exporter; SPICE_SIMULATOR* m_simulator; - SIM_THREAD* m_simThread; - wxCriticalSection m_simThreadCS; - - friend class SIM_THREAD; }; -wxDEFINE_EVENT( wxEVT_SIM_REPORT, wxThreadEvent ); -wxDEFINE_EVENT( wxEVT_SIM_FINISHED, wxThreadEvent ); +wxDEFINE_EVENT( wxEVT_SIM_REPORT, wxCommandEvent ); +wxDEFINE_EVENT( wxEVT_SIM_STARTED, wxCommandEvent ); +wxDEFINE_EVENT( wxEVT_SIM_FINISHED, wxCommandEvent ); #endif // __sim_plot_frame__ diff --git a/eeschema/sim/sim_plot_panel.cpp b/eeschema/sim/sim_plot_panel.cpp index 093803df27..7b330288c8 100644 --- a/eeschema/sim/sim_plot_panel.cpp +++ b/eeschema/sim/sim_plot_panel.cpp @@ -32,7 +32,7 @@ SIM_PLOT_PANEL::SIM_PLOT_PANEL( wxWindow* parent, wxWindowID id, const wxPoint& : wxMathGL( parent, id, pos, size, style, name ), m_painter( this ) { AutoResize = true; - resetRanges(); + ResetAxisRanges(); SetDraw( &m_painter ); } @@ -105,12 +105,12 @@ void SIM_PLOT_PANEL::AddTrace( const wxString& aSpiceName, const wxString& aTitl void SIM_PLOT_PANEL::DeleteTraces() { m_traces.clear(); - resetRanges(); + ResetAxisRanges(); Update(); } -void SIM_PLOT_PANEL::resetRanges() +void SIM_PLOT_PANEL::ResetAxisRanges() { // Set ranges to inverted values, so when there is a new plot added, it will // overridden with correct values diff --git a/eeschema/sim/sim_plot_panel.h b/eeschema/sim/sim_plot_panel.h index 45ceb5c7a3..b71bf6ea32 100644 --- a/eeschema/sim/sim_plot_panel.h +++ b/eeschema/sim/sim_plot_panel.h @@ -79,6 +79,8 @@ public: return m_traces; } + void ResetAxisRanges(); + private: SIM_PLOT_PAINTER m_painter; @@ -88,8 +90,6 @@ private: // Axis ranges determined by the added traces data std::pair m_axisRangeX, m_axisRangeY; - void resetRanges(); - friend class SIM_PLOT_PAINTER; }; diff --git a/eeschema/sim/spice_reporter.h b/eeschema/sim/spice_reporter.h new file mode 100644 index 0000000000..6b0e0b5bfc --- /dev/null +++ b/eeschema/sim/spice_reporter.h @@ -0,0 +1,51 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2016 CERN + * @author Maciej Suminski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef SPICE_REPORTER_H +#define SPICE_REPORTER_H + +#include + +class SPICE_SIMULATOR; + +enum SIM_STATE +{ + SIM_IDLE, + SIM_RUNNING +}; + +/** + * @brief Interface to receive simulation updates from SPICE_SIMULATOR class. + */ +class SPICE_REPORTER : public REPORTER +{ +public: + virtual ~SPICE_REPORTER() + { + } + + virtual void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) = 0; +}; + +#endif /* SPICE_REPORTER_H */ diff --git a/eeschema/sim/spice_simulator.h b/eeschema/sim/spice_simulator.h index 9d34540eae..ead779e3c3 100644 --- a/eeschema/sim/spice_simulator.h +++ b/eeschema/sim/spice_simulator.h @@ -28,7 +28,7 @@ #include #include -class REPORTER; +class SPICE_REPORTER; enum SIM_TRACE_TYPE { @@ -39,10 +39,10 @@ enum SIM_TRACE_TYPE SIM_TR_FFT = 0x10 }; -class SPICE_SIMULATOR { - +class SPICE_SIMULATOR +{ public: - SPICE_SIMULATOR() : m_consoleReporter( NULL ) {} + SPICE_SIMULATOR() : m_reporter( NULL ) {} virtual ~SPICE_SIMULATOR() {} static SPICE_SIMULATOR* CreateInstance( const std::string& aName ); @@ -50,17 +50,19 @@ public: virtual void Init() = 0; virtual bool LoadNetlist( const std::string& aNetlist ) = 0; virtual bool Run() = 0; + virtual bool Stop() = 0; + virtual bool IsRunning() = 0; virtual bool Command( const std::string& aCmd ) = 0; - virtual void SetConsoleReporter( REPORTER* aReporter ) + virtual void SetReporter( SPICE_REPORTER* aReporter ) { - m_consoleReporter = aReporter; + m_reporter = aReporter; } virtual const std::vector GetPlot( const std::string& aName, int aMaxLen = -1 ) = 0; protected: - REPORTER* m_consoleReporter; + SPICE_REPORTER* m_reporter; }; #endif /* SPICE_SIMULATOR_H */