You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

448 lines
13 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <wx/ffile.h>
  24. #include <wx/filedlg.h>
  25. #include <wx_filename.h>
  26. #include <wx/stc/stc.h>
  27. #include <kiway.h>
  28. #include <confirm.h>
  29. #include <wildcards_and_files_ext.h>
  30. #include <project/project_file.h>
  31. #include <sch_edit_frame.h>
  32. #include <sim/sim_plot_frame.h>
  33. #include <tool/tool_manager.h>
  34. #include <tools/ee_actions.h>
  35. #include <tools/simulator_control.h>
  36. #include <scintilla_tricks.h>
  37. #include <dialogs/dialog_user_defined_signals.h>
  38. bool SIMULATOR_CONTROL::Init()
  39. {
  40. Reset( MODEL_RELOAD );
  41. return true;
  42. }
  43. void SIMULATOR_CONTROL::Reset( RESET_REASON aReason )
  44. {
  45. m_plotFrame = getEditFrame<SIM_PLOT_FRAME>();
  46. if( m_plotFrame )
  47. {
  48. m_schematicFrame = m_plotFrame->GetSchematicFrame();
  49. m_circuitModel = m_plotFrame->GetCircuitModel();
  50. m_simulator = m_plotFrame->GetSimulator();
  51. }
  52. }
  53. int SIMULATOR_CONTROL::NewPlot( const TOOL_EVENT& aEvent )
  54. {
  55. SIM_TYPE type = m_circuitModel->GetSimType();
  56. if( SIM_PANEL_BASE::IsPlottable( type ) )
  57. m_plotFrame->NewPlotPanel( m_circuitModel->GetSimCommand(), m_circuitModel->GetSimOptions() );
  58. return 0;
  59. }
  60. int SIMULATOR_CONTROL::OpenWorkbook( const TOOL_EVENT& aEvent )
  61. {
  62. wxFileDialog openDlg( m_plotFrame, _( "Open simulation workbook" ), getDefaultPath(), "",
  63. WorkbookFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
  64. if( openDlg.ShowModal() == wxID_CANCEL )
  65. return -1;
  66. m_plotFrame->LoadWorkbook( openDlg.GetPath() );
  67. return 0;
  68. }
  69. wxString SIMULATOR_CONTROL::getDefaultFilename()
  70. {
  71. wxFileName filename = m_simulator->Settings()->GetWorkbookFilename();
  72. if( filename.GetName().IsEmpty() )
  73. {
  74. if( m_plotFrame->Prj().GetProjectName().IsEmpty() )
  75. {
  76. filename.SetName( _( "noname" ) );
  77. filename.SetExt( WorkbookFileExtension );
  78. }
  79. else
  80. {
  81. filename.SetName( m_plotFrame->Prj().GetProjectName() );
  82. filename.SetExt( WorkbookFileExtension );
  83. }
  84. }
  85. return filename.GetFullName();
  86. }
  87. wxString SIMULATOR_CONTROL::getDefaultPath()
  88. {
  89. wxFileName path = m_simulator->Settings()->GetWorkbookFilename();
  90. path.Normalize( FN_NORMALIZE_FLAGS|wxPATH_NORM_ENV_VARS, m_plotFrame->Prj().GetProjectPath() );
  91. return path.GetPath();
  92. }
  93. int SIMULATOR_CONTROL::SaveWorkbook( const TOOL_EVENT& aEvent )
  94. {
  95. wxString filename;
  96. if( aEvent.IsAction( &EE_ACTIONS::saveWorkbook ) )
  97. filename = m_simulator->Settings()->GetWorkbookFilename();
  98. if( filename.IsEmpty() )
  99. {
  100. wxFileDialog saveAsDlg( m_plotFrame, _( "Save Simulation Workbook As" ), getDefaultPath(),
  101. getDefaultFilename(), WorkbookFileWildcard(),
  102. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  103. if( saveAsDlg.ShowModal() == wxID_CANCEL )
  104. return -1;
  105. filename = saveAsDlg.GetPath();
  106. }
  107. m_plotFrame->SaveWorkbook( m_plotFrame->Prj().AbsolutePath( filename ) );
  108. return 0;
  109. }
  110. int SIMULATOR_CONTROL::ExportPlotAsPNG( const TOOL_EVENT& aEvent )
  111. {
  112. if( !m_plotFrame->GetCurrentPlot() )
  113. return -1;
  114. wxFileDialog saveDlg( m_plotFrame, _( "Save Plot as Image" ), "", "", PngFileWildcard(),
  115. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  116. if( saveDlg.ShowModal() == wxID_CANCEL )
  117. return -1;
  118. m_plotFrame->GetCurrentPlot()->GetPlotWin()->SaveScreenshot( saveDlg.GetPath(),
  119. wxBITMAP_TYPE_PNG );
  120. return 0;
  121. }
  122. int SIMULATOR_CONTROL::ExportPlotAsCSV( const TOOL_EVENT& aEvent )
  123. {
  124. if( !m_plotFrame->GetCurrentPlot() )
  125. return -1;
  126. const wxChar SEPARATOR = ';';
  127. wxFileDialog saveDlg( m_plotFrame, _( "Save Plot Data" ), "", "", CsvFileWildcard(),
  128. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  129. if( saveDlg.ShowModal() == wxID_CANCEL )
  130. return -1;
  131. wxFFile out( saveDlg.GetPath(), "wb" );
  132. std::map<wxString, TRACE*> traces = m_plotFrame->GetCurrentPlot()->GetTraces();
  133. if( traces.size() == 0 )
  134. return -1;
  135. SIM_TYPE simType = m_circuitModel->GetSimType();
  136. std::size_t rowCount = traces.begin()->second->GetDataX().size();
  137. // write column header names on the first row
  138. wxString xAxisName( m_simulator->GetXAxis( simType ) );
  139. out.Write( wxString::Format( wxT( "%s%c" ), xAxisName, SEPARATOR ) );
  140. for( const auto& [name, trace] : traces )
  141. out.Write( wxString::Format( wxT( "%s%c" ), name, SEPARATOR ) );
  142. out.Write( wxS( "\r\n" ) );
  143. // write each row's numerical value
  144. for ( std::size_t curRow=0; curRow < rowCount; curRow++ )
  145. {
  146. double xAxisValue = traces.begin()->second->GetDataX().at( curRow );
  147. out.Write( wxString::Format( wxT( "%g%c" ), xAxisValue, SEPARATOR ) );
  148. for( const auto& [name, trace] : traces )
  149. {
  150. double yAxisValue = trace->GetDataY().at( curRow );
  151. out.Write( wxString::Format( wxT( "%g%c" ), yAxisValue, SEPARATOR ) );
  152. }
  153. out.Write( wxS( "\r\n" ) );
  154. }
  155. out.Close();
  156. return 0;
  157. }
  158. int SIMULATOR_CONTROL::Close( const TOOL_EVENT& aEvent )
  159. {
  160. m_plotFrame->Close();
  161. return 0;
  162. }
  163. int SIMULATOR_CONTROL::Zoom( const TOOL_EVENT& aEvent )
  164. {
  165. if( m_plotFrame->GetCurrentPlot() )
  166. {
  167. if( aEvent.IsAction( &ACTIONS::zoomInCenter ) )
  168. m_plotFrame->GetCurrentPlot()->GetPlotWin()->ZoomIn();
  169. else if( aEvent.IsAction( &ACTIONS::zoomOutCenter ) )
  170. m_plotFrame->GetCurrentPlot()->GetPlotWin()->ZoomOut();
  171. else if( aEvent.IsAction( &ACTIONS::zoomFitScreen ) )
  172. m_plotFrame->GetCurrentPlot()->GetPlotWin()->Fit();
  173. }
  174. return 0;
  175. }
  176. int SIMULATOR_CONTROL::ToggleGrid( const TOOL_EVENT& aEvent )
  177. {
  178. SIM_PLOT_PANEL* plot = m_plotFrame->GetCurrentPlot();
  179. if( plot )
  180. {
  181. plot->ShowGrid( !plot->IsGridShown() );
  182. m_plotFrame->OnModify();
  183. }
  184. return 0;
  185. }
  186. int SIMULATOR_CONTROL::ToggleLegend( const TOOL_EVENT& aEvent )
  187. {
  188. SIM_PLOT_PANEL* plot = m_plotFrame->GetCurrentPlot();
  189. if( plot )
  190. {
  191. plot->ShowLegend( !plot->IsLegendShown() );
  192. m_plotFrame->OnModify();
  193. }
  194. return 0;
  195. }
  196. int SIMULATOR_CONTROL::ToggleDottedSecondary( const TOOL_EVENT& aEvent )
  197. {
  198. SIM_PLOT_PANEL* plot = m_plotFrame->GetCurrentPlot();
  199. if( plot )
  200. {
  201. plot->SetDottedSecondary( !plot->GetDottedSecondary() );
  202. m_plotFrame->OnModify();
  203. }
  204. return 0;
  205. }
  206. int SIMULATOR_CONTROL::ToggleDarkModePlots( const TOOL_EVENT& aEvent )
  207. {
  208. m_plotFrame->ToggleDarkModePlots();
  209. return 0;
  210. }
  211. int SIMULATOR_CONTROL::EditSimCommand( const TOOL_EVENT& aEvent )
  212. {
  213. m_plotFrame->EditSimCommand();
  214. return 0;
  215. }
  216. int SIMULATOR_CONTROL::RunSimulation( const TOOL_EVENT& aEvent )
  217. {
  218. if( m_simulator->IsRunning() )
  219. m_simulator->Stop();
  220. else
  221. m_plotFrame->StartSimulation();
  222. return 0;
  223. }
  224. int SIMULATOR_CONTROL::Probe( const TOOL_EVENT& aEvent )
  225. {
  226. if( m_schematicFrame == nullptr )
  227. return -1;
  228. wxWindow* blocking_dialog = m_schematicFrame->Kiway().GetBlockingDialog();
  229. if( blocking_dialog )
  230. blocking_dialog->Close( true );
  231. m_schematicFrame->GetToolManager()->RunAction( EE_ACTIONS::simProbe );
  232. m_schematicFrame->Raise();
  233. return 0;
  234. }
  235. int SIMULATOR_CONTROL::Tune( const TOOL_EVENT& aEvent )
  236. {
  237. if( m_schematicFrame == nullptr )
  238. return -1;
  239. wxWindow* blocking_dialog = m_schematicFrame->Kiway().GetBlockingDialog();
  240. if( blocking_dialog )
  241. blocking_dialog->Close( true );
  242. m_schematicFrame->GetToolManager()->RunAction( EE_ACTIONS::simTune );
  243. m_schematicFrame->Raise();
  244. return 0;
  245. }
  246. class NETLIST_VIEW_DIALOG : public DIALOG_SHIM
  247. {
  248. public:
  249. enum
  250. {
  251. MARGIN_LINE_NUMBERS
  252. };
  253. void onClose( wxCloseEvent& evt )
  254. {
  255. wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL ) );
  256. }
  257. NETLIST_VIEW_DIALOG( wxWindow* parent, const wxString& source) :
  258. DIALOG_SHIM( parent, wxID_ANY, _( "SPICE Netlist" ), wxDefaultPosition,
  259. wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER )
  260. {
  261. wxStyledTextCtrl* textCtrl = new wxStyledTextCtrl( this, wxID_ANY );
  262. textCtrl->SetMinSize( wxSize( 600, 400 ) );
  263. textCtrl->SetMarginWidth( MARGIN_LINE_NUMBERS, 50 );
  264. textCtrl->StyleSetForeground( wxSTC_STYLE_LINENUMBER, wxColour( 75, 75, 75 ) );
  265. textCtrl->StyleSetBackground( wxSTC_STYLE_LINENUMBER, wxColour( 220, 220, 220 ) );
  266. textCtrl->SetMarginType( MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER );
  267. wxFont fixedFont = KIUI::GetMonospacedUIFont();
  268. for( int i = 0; i < wxSTC_STYLE_MAX; ++i )
  269. textCtrl->StyleSetFont( i, fixedFont );
  270. textCtrl->StyleClearAll(); // Addresses a bug in wx3.0 where styles are not correctly set
  271. textCtrl->SetWrapMode( wxSTC_WRAP_WORD );
  272. textCtrl->SetText( source );
  273. textCtrl->SetLexer( wxSTC_LEX_SPICE );
  274. wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
  275. sizer->Add( textCtrl, 1, wxEXPAND | wxALL, 5 );
  276. SetSizer( sizer );
  277. Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( NETLIST_VIEW_DIALOG::onClose ),
  278. nullptr, this );
  279. m_scintillaTricks = std::make_unique<SCINTILLA_TRICKS>( textCtrl, wxT( "{}" ), false );
  280. finishDialogSettings();
  281. }
  282. std::unique_ptr<SCINTILLA_TRICKS> m_scintillaTricks;
  283. };
  284. int SIMULATOR_CONTROL::EditUserDefinedSignals( const TOOL_EVENT& aEvent )
  285. {
  286. std::map<int, wxString> userSignals = m_plotFrame->UserDefinedSignals();
  287. DIALOG_USER_DEFINED_SIGNALS dlg( m_plotFrame, &userSignals );
  288. if( dlg.ShowQuasiModal() == wxID_OK )
  289. m_plotFrame->SetUserDefinedSignals( userSignals );
  290. return 0;
  291. }
  292. int SIMULATOR_CONTROL::ShowNetlist( const TOOL_EVENT& aEvent )
  293. {
  294. if( m_schematicFrame == nullptr || m_simulator == nullptr )
  295. return -1;
  296. wxString errors;
  297. WX_STRING_REPORTER reporter( &errors );
  298. STRING_FORMATTER formatter;
  299. m_circuitModel->SetSimOptions( m_plotFrame->GetCurrentOptions() );
  300. m_circuitModel->GetNetlist( &formatter, reporter );
  301. NETLIST_VIEW_DIALOG dlg( m_plotFrame, errors.IsEmpty() ? wxString( formatter.GetString() ) : errors );
  302. dlg.ShowModal();
  303. return 0;
  304. }
  305. void SIMULATOR_CONTROL::setTransitions()
  306. {
  307. Go( &SIMULATOR_CONTROL::NewPlot, EE_ACTIONS::newPlot.MakeEvent() );
  308. Go( &SIMULATOR_CONTROL::OpenWorkbook, EE_ACTIONS::openWorkbook.MakeEvent() );
  309. Go( &SIMULATOR_CONTROL::SaveWorkbook, EE_ACTIONS::saveWorkbook.MakeEvent() );
  310. Go( &SIMULATOR_CONTROL::SaveWorkbook, EE_ACTIONS::saveWorkbookAs.MakeEvent() );
  311. Go( &SIMULATOR_CONTROL::ExportPlotAsPNG, EE_ACTIONS::exportPlotAsPNG.MakeEvent() );
  312. Go( &SIMULATOR_CONTROL::ExportPlotAsCSV, EE_ACTIONS::exportPlotAsCSV.MakeEvent() );
  313. Go( &SIMULATOR_CONTROL::Close, ACTIONS::quit.MakeEvent() );
  314. Go( &SIMULATOR_CONTROL::Zoom, ACTIONS::zoomInCenter.MakeEvent() );
  315. Go( &SIMULATOR_CONTROL::Zoom, ACTIONS::zoomOutCenter.MakeEvent() );
  316. Go( &SIMULATOR_CONTROL::Zoom, ACTIONS::zoomFitScreen.MakeEvent() );
  317. Go( &SIMULATOR_CONTROL::ToggleGrid, ACTIONS::toggleGrid.MakeEvent() );
  318. Go( &SIMULATOR_CONTROL::ToggleLegend, EE_ACTIONS::toggleLegend.MakeEvent() );
  319. Go( &SIMULATOR_CONTROL::ToggleDottedSecondary, EE_ACTIONS::toggleDottedSecondary.MakeEvent() );
  320. Go( &SIMULATOR_CONTROL::ToggleDarkModePlots, EE_ACTIONS::toggleDarkModePlots.MakeEvent() );
  321. Go( &SIMULATOR_CONTROL::EditSimCommand, EE_ACTIONS::simCommand.MakeEvent() );
  322. Go( &SIMULATOR_CONTROL::RunSimulation, EE_ACTIONS::runSimulation.MakeEvent() );
  323. Go( &SIMULATOR_CONTROL::RunSimulation, EE_ACTIONS::stopSimulation.MakeEvent() );
  324. Go( &SIMULATOR_CONTROL::Probe, EE_ACTIONS::simProbe.MakeEvent() );
  325. Go( &SIMULATOR_CONTROL::Tune, EE_ACTIONS::simTune.MakeEvent() );
  326. Go( &SIMULATOR_CONTROL::EditUserDefinedSignals, EE_ACTIONS::editUserDefinedSignals.MakeEvent() );
  327. Go( &SIMULATOR_CONTROL::ShowNetlist, EE_ACTIONS::showNetlist.MakeEvent() );
  328. }