Move sim workbook to json.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/15197
This commit is contained in:
Jeff Young 2023-07-14 17:44:05 +01:00
parent ff044cd0cf
commit 054252ccbe
4 changed files with 588 additions and 404 deletions

View File

@ -223,19 +223,21 @@ set( EESCHEMA_SIM_SRCS
sim/kibis/ibis_parser.cpp sim/kibis/ibis_parser.cpp
sim/kibis/kibis.cpp sim/kibis/kibis.cpp
sim/spice_circuit_model.cpp sim/spice_circuit_model.cpp
sim/ngspice.cpp sim/ngspice.cpp
sim/simulator_frame.cpp sim/simulator_frame.cpp
sim/simulator_frame_ui.cpp sim/simulator_frame_ui.cpp
sim/simulator_frame_ui_base.cpp sim/simulator_frame_ui_base.cpp
sim/sim_plot_colors.cpp sim/sim_plot_colors.cpp
sim/sim_tab.cpp sim/sim_plot_tab.cpp
sim/sim_plot_tab.cpp
sim/sim_property.cpp sim/sim_property.cpp
sim/sim_tab.cpp
sim/spice_simulator.cpp sim/spice_simulator.cpp
sim/spice_value.cpp sim/spice_value.cpp
sim/toolbars_simulator_frame.cpp sim/toolbars_simulator_frame.cpp
sim/legacy_workbook.cpp
widgets/sim_notebook.cpp widgets/sim_notebook.cpp
widgets/tuner_slider.cpp widgets/tuner_slider.cpp
widgets/tuner_slider_base.cpp widgets/tuner_slider_base.cpp

View File

@ -0,0 +1,350 @@
/*
* 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 <memory>
#include <wx/debug.h>
#include <project/project_file.h>
#include <confirm.h>
#include <string_utils.h>
#include <sim/simulator_frame_ui.h>
#include <sim/simulator_frame.h>
#include <sim/sim_plot_tab.h>
void SIMULATOR_FRAME_UI::parseTraceParams( SIM_PLOT_TAB* aPlotTab, TRACE* aTrace,
const wxString& aSignalName, const wxString& aParams )
{
auto addCursor =
[&]( int aCursorId, double x )
{
CURSOR* cursor = new CURSOR( aTrace, aPlotTab );
cursor->SetName( aSignalName );
cursor->SetPen( wxPen( aTrace->GetTraceColour() ) );
cursor->SetCoordX( x );
aTrace->SetCursor( aCursorId, cursor );
aPlotTab->GetPlotWin()->AddLayer( cursor );
};
wxArrayString items = wxSplit( aParams, '|' );
for( const wxString& item : items )
{
if( item.StartsWith( wxS( "rgb" ) ) )
{
wxColour color;
color.Set( item );
aTrace->SetTraceColour( color );
aPlotTab->UpdateTraceStyle( aTrace );
}
else if( item.StartsWith( wxS( "cursor1" ) ) )
{
wxArrayString parts = wxSplit( item, ':' );
double val;
if( parts.size() == 3 )
{
parts[0].AfterFirst( '=' ).ToDouble( &val );
m_cursorFormats[0][0].FromString( parts[1] );
m_cursorFormats[0][1].FromString( parts[2] );
addCursor( 1, val );
}
}
else if( item.StartsWith( wxS( "cursor2" ) ) )
{
wxArrayString parts = wxSplit( item, ':' );
double val;
if( parts.size() == 3 )
{
parts[0].AfterFirst( '=' ).ToDouble( &val );
m_cursorFormats[1][0].FromString( parts[1] );
m_cursorFormats[1][1].FromString( parts[2] );
addCursor( 2, val );
}
}
else if( item.StartsWith( wxS( "cursorD" ) ) )
{
wxArrayString parts = wxSplit( item, ':' );
if( parts.size() == 3 )
{
m_cursorFormats[2][0].FromString( parts[1] );
m_cursorFormats[2][1].FromString( parts[2] );
}
}
else if( item == wxS( "dottedSecondary" ) )
{
aPlotTab->SetDottedSecondary( true );
}
else if( item == wxS( "hideGrid" ) )
{
aPlotTab->ShowGrid( false );
}
}
}
bool SIMULATOR_FRAME_UI::loadLegacyWorkbook( const wxString& aPath )
{
wxTextFile file( aPath );
#define EXPECTING( msg ) \
DisplayErrorMessage( this, wxString::Format( _( "Error loading workbook: line %d: %s." ), \
file.GetCurrentLine()+1, \
msg ) )
if( !file.Open() )
return false;
long version = 1;
wxString firstLine = file.GetFirstLine();
wxString pageCountLine;
if( firstLine.StartsWith( wxT( "version " ) ) )
{
if( !firstLine.substr( 8 ).ToLong( &version ) )
{
EXPECTING( _( "expecting version" ) );
file.Close();
return false;
}
pageCountLine = file.GetNextLine();
}
else
{
pageCountLine = firstLine;
}
long tabCount;
if( !pageCountLine.ToLong( &tabCount ) )
{
EXPECTING( _( "expecting simulation tab count" ) );
file.Close();
return false;
}
std::map<SIM_PLOT_TAB*, std::vector<std::tuple<long, wxString, wxString>>> traceInfo;
for( long i = 0; i < tabCount; ++i )
{
long simType, tracesCount;
if( !file.GetNextLine().ToLong( &simType ) )
{
EXPECTING( _( "expecting simulation tab type" ) );
file.Close();
return false;
}
wxString command = UnescapeString( file.GetNextLine() );
wxString simCommand;
int simOptions = NETLIST_EXPORTER_SPICE::OPTION_DEFAULT_FLAGS;
wxStringTokenizer tokenizer( command, wxT( "\r\n" ), wxTOKEN_STRTOK );
if( version >= 2 )
{
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS;
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
}
if( version >= 3 )
{
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
}
while( tokenizer.HasMoreTokens() )
{
wxString line = tokenizer.GetNextToken();
if( line.StartsWith( wxT( ".kicad adjustpaths" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS;
else if( line.StartsWith( wxT( ".save all" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
else if( line.StartsWith( wxT( ".probe alli" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
else if( line.StartsWith( wxT( ".probe allp" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
else
simCommand += line + wxT( "\n" );
}
SIM_TAB* simTab = NewSimTab( simCommand, simOptions );
SIM_PLOT_TAB* plotTab = dynamic_cast<SIM_PLOT_TAB*>( simTab );
if( !file.GetNextLine().ToLong( &tracesCount ) )
{
EXPECTING( _( "expecting trace count" ) );
file.Close();
return false;
}
if( plotTab )
traceInfo[plotTab] = {};
for( long j = 0; j < tracesCount; ++j )
{
long traceType;
wxString name, param;
if( !file.GetNextLine().ToLong( &traceType ) )
{
EXPECTING( _( "expecting trace type" ) );
file.Close();
return false;
}
name = file.GetNextLine();
if( name.IsEmpty() )
{
EXPECTING( _( "expecting trace name" ) );
file.Close();
return false;
}
param = file.GetNextLine();
if( param.IsEmpty() )
{
EXPECTING( _( "expecting trace color" ) );
file.Close();
return false;
}
if( plotTab )
traceInfo[plotTab].emplace_back( std::make_tuple( traceType, name, param ) );
}
if( version > 4 )
{
long measurementCount;
if( !file.GetNextLine().ToLong( &measurementCount ) )
{
EXPECTING( _( "expecting measurement count" ) );
file.Close();
return false;
}
for( int ii = 0; ii < (int) measurementCount; ++ ii )
{
wxString measurement = file.GetNextLine();
if( measurement.IsEmpty() )
{
EXPECTING( _( "expecting measurement definition" ) );
file.Close();
return false;
}
wxString format = file.GetNextLine();
if( format.IsEmpty() )
{
EXPECTING( _( "expecting measurement format definition" ) );
file.Close();
return false;
}
if( plotTab )
plotTab->Measurements().emplace_back( measurement, format );
}
}
}
long userDefinedSignalCount;
if( file.GetNextLine().ToLong( &userDefinedSignalCount ) )
{
for( int ii = 0; ii < (int) userDefinedSignalCount; ++ii )
m_userDefinedSignals[ ii ] = file.GetNextLine();
}
for( const auto& [ plotTab, traceInfoVector ] : traceInfo )
{
for( const auto& [ traceType, signalName, param ] : traceInfoVector )
{
if( traceType == SPT_UNKNOWN && signalName == wxS( "$LEGEND" ) )
{
wxArrayString coords = wxSplit( param, ' ' );
if( coords.size() >= 2 )
{
long x = 0;
long y = 0;
coords[0].ToLong( &x );
coords[1].ToLong( &y );
plotTab->SetLegendPosition( wxPoint( (int) x, (int) y ) );
}
plotTab->ShowLegend( true );
}
else
{
wxString vectorName = vectorNameFromSignalName( plotTab, signalName, nullptr );
TRACE* trace = plotTab->AddTrace( vectorName, (int) traceType );
if( version >= 4 && trace )
parseTraceParams( plotTab, trace, signalName, param );
}
}
plotTab->UpdatePlotColors();
}
if( SIM_TAB* simTab = GetCurrentSimTab() )
{
m_simulatorFrame->LoadSimulator( simTab->GetSimCommand(), simTab->GetSimOptions() );
if( version >= 5 )
{
simTab = dynamic_cast<SIM_TAB*>( m_plotNotebook->GetPage( 0 ) );
simTab->SetLastSchTextSimCommand( file.GetNextLine() );
}
}
file.Close();
return true;
}

View File

@ -24,6 +24,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
#include <memory>
#include <fmt/format.h>
#include <wx/wfstream.h>
#include <wx/stdstream.h>
#include <wx/debug.h> #include <wx/debug.h>
#include <project/project_file.h> #include <project/project_file.h>
@ -41,13 +46,10 @@
#include <sim/simulator_frame.h> #include <sim/simulator_frame.h>
#include <sim/sim_plot_tab.h> #include <sim/sim_plot_tab.h>
#include <sim/spice_simulator.h> #include <sim/spice_simulator.h>
#include "fmt/format.h"
#include <dialogs/dialog_text_entry.h> #include <dialogs/dialog_text_entry.h>
#include <dialogs/dialog_sim_format_value.h> #include <dialogs/dialog_sim_format_value.h>
#include <eeschema_settings.h> #include <eeschema_settings.h>
#include <memory>
SIM_TRACE_TYPE operator|( SIM_TRACE_TYPE aFirst, SIM_TRACE_TYPE aSecond ) SIM_TRACE_TYPE operator|( SIM_TRACE_TYPE aFirst, SIM_TRACE_TYPE aSecond )
{ {
@ -1764,314 +1766,31 @@ void SIMULATOR_FRAME_UI::applyTuners()
} }
void SIMULATOR_FRAME_UI::parseTraceParams( SIM_PLOT_TAB* aPlotTab, TRACE* aTrace,
const wxString& aSignalName, const wxString& aParams )
{
auto addCursor =
[&]( int aCursorId, double x )
{
CURSOR* cursor = new CURSOR( aTrace, aPlotTab );
cursor->SetName( aSignalName );
cursor->SetPen( wxPen( aTrace->GetTraceColour() ) );
cursor->SetCoordX( x );
aTrace->SetCursor( aCursorId, cursor );
aPlotTab->GetPlotWin()->AddLayer( cursor );
};
wxArrayString items = wxSplit( aParams, '|' );
for( const wxString& item : items )
{
if( item.StartsWith( wxS( "rgb" ) ) )
{
wxColour color;
color.Set( item );
aTrace->SetTraceColour( color );
aPlotTab->UpdateTraceStyle( aTrace );
}
else if( item.StartsWith( wxS( "cursor1" ) ) )
{
wxArrayString parts = wxSplit( item, ':' );
double val;
if( parts.size() == 3 )
{
parts[0].AfterFirst( '=' ).ToDouble( &val );
m_cursorFormats[0][0].FromString( parts[1] );
m_cursorFormats[0][1].FromString( parts[2] );
addCursor( 1, val );
}
}
else if( item.StartsWith( wxS( "cursor2" ) ) )
{
wxArrayString parts = wxSplit( item, ':' );
double val;
if( parts.size() == 3 )
{
parts[0].AfterFirst( '=' ).ToDouble( &val );
m_cursorFormats[1][0].FromString( parts[1] );
m_cursorFormats[1][1].FromString( parts[2] );
addCursor( 2, val );
}
}
else if( item.StartsWith( wxS( "cursorD" ) ) )
{
wxArrayString parts = wxSplit( item, ':' );
if( parts.size() == 3 )
{
m_cursorFormats[2][0].FromString( parts[1] );
m_cursorFormats[2][1].FromString( parts[2] );
}
}
else if( item == wxS( "dottedSecondary" ) )
{
aPlotTab->SetDottedSecondary( true );
}
else if( item == wxS( "hideGrid" ) )
{
aPlotTab->ShowGrid( false );
}
}
}
bool SIMULATOR_FRAME_UI::LoadWorkbook( const wxString& aPath ) bool SIMULATOR_FRAME_UI::LoadWorkbook( const wxString& aPath )
{ {
m_plotNotebook->DeleteAllPages();
wxTextFile file( aPath ); wxTextFile file( aPath );
#define EXPECTING( msg ) \
DisplayErrorMessage( this, wxString::Format( _( "Error loading workbook: line %d: %s." ), \
file.GetCurrentLine()+1, \
msg ) )
if( !file.Open() ) if( !file.Open() )
return false; return false;
long version = 1;
wxString firstLine = file.GetFirstLine(); wxString firstLine = file.GetFirstLine();
wxString pageCountLine; long dummy;
bool legacy = firstLine.StartsWith( wxT( "version " ) ) || firstLine.ToLong( &dummy );
if( firstLine.StartsWith( wxT( "version " ) ) ) file.Close();
m_plotNotebook->DeleteAllPages();
m_userDefinedSignals.clear();
if( legacy )
{ {
if( !firstLine.substr( 8 ).ToLong( &version ) ) if( !loadLegacyWorkbook( aPath ) )
{
EXPECTING( _( "expecting version" ) );
file.Close();
return false; return false;
}
pageCountLine = file.GetNextLine();
} }
else else
{ {
pageCountLine = firstLine; if( !loadJsonWorkbook( aPath ) )
}
long pageCount;
if( !pageCountLine.ToLong( &pageCount ) )
{
EXPECTING( _( "expecting simulation tab count" ) );
file.Close();
return false;
}
std::map<SIM_PLOT_TAB*, std::vector<std::tuple<long, wxString, wxString>>> traceInfo;
for( long i = 0; i < pageCount; ++i )
{
long simType, tracesCount;
if( !file.GetNextLine().ToLong( &simType ) )
{
EXPECTING( _( "expecting simulation tab type" ) );
file.Close();
return false; return false;
}
wxString command = UnescapeString( file.GetNextLine() );
wxString simCommand;
int simOptions = NETLIST_EXPORTER_SPICE::OPTION_DEFAULT_FLAGS;
wxStringTokenizer tokenizer( command, wxT( "\r\n" ), wxTOKEN_STRTOK );
if( version >= 2 )
{
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS;
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
}
if( version >= 3 )
{
simOptions &= ~NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
}
while( tokenizer.HasMoreTokens() )
{
wxString line = tokenizer.GetNextToken();
if( line.StartsWith( wxT( ".kicad adjustpaths" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS;
else if( line.StartsWith( wxT( ".save all" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
else if( line.StartsWith( wxT( ".probe alli" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
else if( line.StartsWith( wxT( ".probe allp" ) ) )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
else
simCommand += line + wxT( "\n" );
}
SIM_TAB* simTab = NewSimTab( simCommand, simOptions );
SIM_PLOT_TAB* plotTab = dynamic_cast<SIM_PLOT_TAB*>( simTab );
if( !file.GetNextLine().ToLong( &tracesCount ) )
{
EXPECTING( _( "expecting trace count" ) );
file.Close();
return false;
}
if( plotTab )
traceInfo[plotTab] = {};
for( long j = 0; j < tracesCount; ++j )
{
long traceType;
wxString name, param;
if( !file.GetNextLine().ToLong( &traceType ) )
{
EXPECTING( _( "expecting trace type" ) );
file.Close();
return false;
}
name = file.GetNextLine();
if( name.IsEmpty() )
{
EXPECTING( _( "expecting trace name" ) );
file.Close();
return false;
}
param = file.GetNextLine();
if( param.IsEmpty() )
{
EXPECTING( _( "expecting trace color" ) );
file.Close();
return false;
}
if( plotTab )
traceInfo[plotTab].emplace_back( std::make_tuple( traceType, name, param ) );
}
if( version > 4 )
{
long measurementCount;
if( !file.GetNextLine().ToLong( &measurementCount ) )
{
EXPECTING( _( "expecting measurement count" ) );
file.Close();
return false;
}
for( int ii = 0; ii < (int) measurementCount; ++ ii )
{
wxString measurement = file.GetNextLine();
if( measurement.IsEmpty() )
{
EXPECTING( _( "expecting measurement definition" ) );
file.Close();
return false;
}
wxString format = file.GetNextLine();
if( format.IsEmpty() )
{
EXPECTING( _( "expecting measurement format definition" ) );
file.Close();
return false;
}
if( plotTab )
plotTab->Measurements().emplace_back( measurement, format );
}
}
}
long userDefinedSignalCount;
if( file.GetNextLine().ToLong( &userDefinedSignalCount ) )
{
for( int ii = 0; ii < (int) userDefinedSignalCount; ++ii )
m_userDefinedSignals[ ii ] = file.GetNextLine();
}
for( const auto& [plotTab, traceInfoVector ] : traceInfo )
{
for( const auto& [ traceType, signalName, param ] : traceInfoVector )
{
if( traceType == SPT_UNKNOWN && signalName == wxS( "$LEGEND" ) )
{
wxArrayString coords = wxSplit( param, ' ' );
if( coords.size() >= 2 )
{
long x = 0;
long y = 0;
coords[0].ToLong( &x );
coords[1].ToLong( &y );
plotTab->SetLegendPosition( wxPoint( (int) x, (int) y ) );
}
plotTab->ShowLegend( true );
}
else
{
wxString vectorName = vectorNameFromSignalName( plotTab, signalName, nullptr );
TRACE* trace = plotTab->AddTrace( vectorName, (int) traceType );
if( version >= 4 && trace )
parseTraceParams( plotTab, trace, signalName, param );
}
}
plotTab->UpdatePlotColors();
}
if( SIM_TAB* simTab = GetCurrentSimTab() )
{
m_simulatorFrame->LoadSimulator( simTab->GetSimCommand(), simTab->GetSimOptions() );
if( version >= 5 )
{
simTab = dynamic_cast<SIM_TAB*>( m_plotNotebook->GetPage( 0 ) );
simTab->SetLastSchTextSimCommand( file.GetNextLine() );
}
} }
rebuildSignalsList(); rebuildSignalsList();
@ -2081,8 +1800,6 @@ bool SIMULATOR_FRAME_UI::LoadWorkbook( const wxString& aPath )
updatePlotCursors(); updatePlotCursors();
rebuildMeasurementsGrid(); rebuildMeasurementsGrid();
file.Close();
wxFileName filename( aPath ); wxFileName filename( aPath );
filename.MakeRelativeTo( m_schematicFrame->Prj().GetProjectPath() ); filename.MakeRelativeTo( m_schematicFrame->Prj().GetProjectPath() );
@ -2093,160 +1810,272 @@ bool SIMULATOR_FRAME_UI::LoadWorkbook( const wxString& aPath )
} }
bool SIMULATOR_FRAME_UI::loadJsonWorkbook( const wxString& aPath )
{
wxFFileInputStream fp( aPath, wxT( "rt" ) );
wxStdInputStream fstream( fp );
if( !fp.IsOk() )
return false;
try
{
nlohmann::json js = nlohmann::json::parse( fstream, nullptr, true, true );
std::map<SIM_PLOT_TAB*, nlohmann::json> traceInfo;
for( const nlohmann::json& tab_js : js[ "tabs" ] )
{
wxString simCommand;
int simOptions = NETLIST_EXPORTER_SPICE::OPTION_ADJUST_PASSIVE_VALS;
for( const nlohmann::json& cmd : tab_js[ "commands" ] )
{
if( cmd == ".kicad adjustpaths" )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS;
else if( cmd == ".save all" )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES;
else if( cmd == ".probe alli" )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS;
else if( cmd == ".probe allp" )
simOptions |= NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS;
else
simCommand += wxString( cmd ) + wxT( "\n" );
}
SIM_TAB* simTab = NewSimTab( simCommand, simOptions );
SIM_PLOT_TAB* plotTab = dynamic_cast<SIM_PLOT_TAB*>( simTab );
if( plotTab )
{
if( tab_js.contains( "traces" ) )
traceInfo[plotTab] = tab_js[ "traces" ];
if( js.contains( "measurements" ) )
{
for( const nlohmann::json& m_js : tab_js[ "measurements" ] )
plotTab->Measurements().emplace_back( m_js[ "expr" ], m_js[ "format" ] );
}
plotTab->SetDottedSecondary( tab_js[ "dottedSecondary" ] == "true" );
plotTab->ShowGrid( tab_js[ "showGrid" ] == "true" );
}
}
int ii = 0;
if( js.contains( "user_defined_signals" ) )
{
for( const nlohmann::json& signal_js : js[ "user_defined_signals" ] )
m_userDefinedSignals[ii++] = wxString( signal_js );
}
auto addCursor =
[this]( SIM_PLOT_TAB* aPlotTab, TRACE* aTrace, const wxString& aSignalName,
int aCursorId, const nlohmann::json& aCursor_js )
{
if( aCursorId == 1 || aCursorId == 2 )
{
CURSOR* cursor = new CURSOR( aTrace, aPlotTab );
cursor->SetName( aSignalName );
cursor->SetPen( wxPen( aTrace->GetTraceColour() ) );
cursor->SetCoordX( aCursor_js[ "position" ] );
aTrace->SetCursor( aCursorId, cursor );
aPlotTab->GetPlotWin()->AddLayer( cursor );
}
m_cursorFormats[aCursorId-1][0].FromString( aCursor_js[ "x_format" ] );
m_cursorFormats[aCursorId-1][1].FromString( aCursor_js[ "y_format" ] );
};
for( const auto& [ plotTab, traces_js ] : traceInfo )
{
for( const nlohmann::json& trace_js : traces_js )
{
wxString signalName = trace_js[ "signal" ];
wxString vectorName = vectorNameFromSignalName( plotTab, signalName, nullptr );
TRACE* trace = plotTab->AddTrace( vectorName, trace_js[ "trace_type" ] );
if( trace )
{
if( trace_js.contains( "cursor1" ) )
addCursor( plotTab, trace, signalName, 1, trace_js[ "cursor1" ] );
if( trace_js.contains( "cursor2" ) )
addCursor( plotTab, trace, signalName, 2, trace_js[ "cursor2" ] );
if( trace_js.contains( "cursorD" ) )
addCursor( plotTab, trace, signalName, 3, trace_js[ "cursorD" ] );
if( trace_js.contains( "color" ) )
{
wxColour color;
color.Set( wxString( trace_js[ "color" ] ) );
trace->SetTraceColour( color );
plotTab->UpdateTraceStyle( trace );
}
}
}
plotTab->UpdatePlotColors();
}
if( SIM_TAB* simTab = GetCurrentSimTab() )
{
m_simulatorFrame->LoadSimulator( simTab->GetSimCommand(), simTab->GetSimOptions() );
simTab = dynamic_cast<SIM_TAB*>( m_plotNotebook->GetPage( 0 ) );
simTab->SetLastSchTextSimCommand( js[ "last_sch_text_sim_command" ] );
}
}
catch( nlohmann::json::parse_error& error )
{
wxLogTrace( traceSettings, wxT( "Json parse error reading %s: %s" ), aPath, error.what() );
return false;
}
return true;
}
bool SIMULATOR_FRAME_UI::SaveWorkbook( const wxString& aPath ) bool SIMULATOR_FRAME_UI::SaveWorkbook( const wxString& aPath )
{ {
wxFileName filename = aPath; wxFileName filename = aPath;
filename.SetExt( WorkbookFileExtension ); filename.SetExt( WorkbookFileExtension );
wxTextFile file( filename.GetFullPath() ); wxFile file;
if( file.Exists() ) file.Create( filename.GetFullPath(), true /* overwrite */ );
{
if( !file.Open() )
return false;
file.Clear(); if( !file.IsOpened() )
} return false;
else
{
file.Create();
}
file.AddLine( wxT( "version 5" ) ); nlohmann::json tabs_js = nlohmann::json::array();
file.AddLine( wxString::Format( wxT( "%llu" ), m_plotNotebook->GetPageCount() ) );
for( size_t i = 0; i < m_plotNotebook->GetPageCount(); i++ ) for( size_t i = 0; i < m_plotNotebook->GetPageCount(); i++ )
{ {
SIM_TAB* simTab = dynamic_cast<SIM_TAB*>( m_plotNotebook->GetPage( i ) ); SIM_TAB* simTab = dynamic_cast<SIM_TAB*>( m_plotNotebook->GetPage( i ) );
if( !simTab ) if( !simTab )
{
file.AddLine( wxString::Format( wxT( "%llu" ), 0ull ) );
continue; continue;
}
file.AddLine( wxString::Format( wxT( "%d" ), simTab->GetSimType() ) ); nlohmann::json commands_js = nlohmann::json::array();
wxString command = simTab->GetSimCommand(); commands_js.push_back( simTab->GetSimCommand() );
int options = simTab->GetSimOptions();
if( options & NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS ) int options = simTab->GetSimOptions();
command += wxT( "\n.kicad adjustpaths" );
if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES ) if( simTab->GetSimOptions() & NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS )
command += wxT( "\n.save all" ); commands_js.push_back( ".kicad adjustpaths" );
if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS ) if( simTab->GetSimOptions() & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES )
command += wxT( "\n.probe alli" ); commands_js.push_back( ".save all" );
if( options & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS ) if( simTab->GetSimOptions() & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS )
command += wxT( "\n.probe allp" ); commands_js.push_back( ".probe alli" );
file.AddLine( EscapeString( command, CTX_LINE ) ); if( simTab->GetSimOptions() & NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_DISSIPATIONS )
commands_js.push_back( ".probe allp" );
SIM_PLOT_TAB* plotTab = dynamic_cast<SIM_PLOT_TAB*>( simTab ); nlohmann::json tab_js = nlohmann::json( { { "commands", commands_js } } );
if( !plotTab ) if( SIM_PLOT_TAB* plotTab = dynamic_cast<SIM_PLOT_TAB*>( simTab ) )
{ {
file.AddLine( wxString::Format( wxT( "%llu" ), 0ull ) ); nlohmann::json traces_js = nlohmann::json::array();
continue;
}
size_t traceCount = plotTab->GetTraces().size(); auto findSignalName =
[&]( const wxString& aVectorName ) -> wxString
if( plotTab->IsLegendShown() )
traceCount++;
file.AddLine( wxString::Format( wxT( "%llu" ), traceCount ) );
auto findSignalName =
[&]( const wxString& aVectorName ) -> wxString
{
for( const auto& [ id, signal ] : m_userDefinedSignals )
{ {
if( aVectorName == vectorNameFromSignalId( id ) ) for( const auto& [ id, signal ] : m_userDefinedSignals )
return signal; {
} if( aVectorName == vectorNameFromSignalId( id ) )
return signal;
}
return aVectorName; return aVectorName;
}; };
for( const auto& [name, trace] : plotTab->GetTraces() ) for( const auto& [name, trace] : plotTab->GetTraces() )
{
file.AddLine( wxString::Format( wxT( "%d" ), trace->GetType() ) );
file.AddLine( findSignalName( trace->GetName() ) );
wxString msg = COLOR4D( trace->GetTraceColour() ).ToCSSString();
if( CURSOR* cursor = trace->GetCursor( 1 ) )
{ {
msg += wxString::Format( wxS( "|cursor1=%E:%s:%s" ), nlohmann::json trace_js = nlohmann::json(
cursor->GetCoords().x, { { "trace_type", (int) trace->GetType() },
m_cursorFormats[0][0].ToString(), { "signal", findSignalName( trace->GetName() ) },
m_cursorFormats[0][1].ToString() ); { "color", COLOR4D( trace->GetTraceColour() ).ToCSSString() } } );
if( CURSOR* cursor = trace->GetCursor( 1 ) )
{
trace_js["cursor1"] = nlohmann::json(
{ { "position", cursor->GetCoords().x },
{ "x_format", m_cursorFormats[0][0].ToString() },
{ "y_format", m_cursorFormats[0][1].ToString() } } );
}
if( CURSOR* cursor = trace->GetCursor( 2 ) )
{
trace_js["cursor2"] = nlohmann::json(
{ { "position", cursor->GetCoords().x },
{ "x_format", m_cursorFormats[1][0].ToString() },
{ "y_format", m_cursorFormats[1][1].ToString() } } );
}
if( trace->GetCursor( 1 ) || trace->GetCursor( 2 ) )
{
trace_js["cursorD"] = nlohmann::json(
{ { "x_format", m_cursorFormats[2][0].ToString() },
{ "y_format", m_cursorFormats[2][1].ToString() } } );
}
traces_js.push_back( trace_js );
} }
if( CURSOR* cursor = trace->GetCursor( 2 ) ) nlohmann::json measurements_js = nlohmann::json::array();
for( const auto& [ measurement, format ] : plotTab->Measurements() )
{ {
msg += wxString::Format( wxS( "|cursor2=%E:%s:%s" ), measurements_js.push_back( nlohmann::json( { { "expr", measurement },
cursor->GetCoords().x, { "format", format } } ) );
m_cursorFormats[1][0].ToString(),
m_cursorFormats[1][1].ToString() );
} }
if( trace->GetCursor( 1 ) || trace->GetCursor( 2 ) ) tab_js[ "traces" ] = traces_js;
tab_js[ "measurements" ] = measurements_js;
tab_js[ "dottedSecondary" ] = plotTab->GetDottedSecondary();
tab_js[ "showGrid" ] = plotTab->IsGridShown();
if( plotTab->IsLegendShown() )
{ {
msg += wxString::Format( wxS( "|cursorD:%s:%s" ), tab_js[ "legend" ] = nlohmann::json( { { "x", plotTab->GetLegendPosition().x },
m_cursorFormats[2][0].ToString(), { "y", plotTab->GetLegendPosition().y } } );
m_cursorFormats[2][1].ToString() );
} }
if( plotTab->GetDottedSecondary() )
msg += wxS( "|dottedSecondary" );
if( !plotTab->IsGridShown() )
msg += wxS( "|hideGrid" );
file.AddLine( msg );
} }
if( plotTab->IsLegendShown() ) tabs_js.push_back( tab_js );
{
file.AddLine( wxString::Format( wxT( "%d" ), SPT_UNKNOWN ) );
file.AddLine( wxT( "$LEGEND" ) );
file.AddLine( wxString::Format( wxT( "%d %d" ), plotTab->GetLegendPosition().x,
plotTab->GetLegendPosition().y - 40 ) );
}
file.AddLine( wxString::Format( wxT( "%llu" ), plotTab->Measurements().size() ) );
for( const auto& [ measurement, format ] : plotTab->Measurements() )
{
file.AddLine( measurement );
file.AddLine( format );
}
} }
file.AddLine( wxString::Format( wxT( "%llu" ), m_userDefinedSignals.size() ) ); nlohmann::json userDefinedSignals_js = nlohmann::json::array();
for( const auto& [ id, signal ] : m_userDefinedSignals ) for( const auto& [ id, signal ] : m_userDefinedSignals )
file.AddLine( signal ); userDefinedSignals_js.push_back( signal );
nlohmann::json js = nlohmann::json( { { "version", 6 },
{ "tabs", tabs_js },
{ "user_defined_signals", userDefinedSignals_js } } );
// Store the value of any simulation command found on the schematic sheet in a SCH_TEXT // Store the value of any simulation command found on the schematic sheet in a SCH_TEXT
// object. If this changes we want to warn the user and ask them if they want to update // object. If this changes we want to warn the user and ask them if they want to update
// the corresponding panel's sim command. // the corresponding panel's sim command.
wxString lastSchTextSimCommand;
if( m_plotNotebook->GetPageCount() > 0 ) if( m_plotNotebook->GetPageCount() > 0 )
{ {
SIM_TAB* simTab = dynamic_cast<SIM_TAB*>( m_plotNotebook->GetPage( 0 ) ); SIM_TAB* simTab = dynamic_cast<SIM_TAB*>( m_plotNotebook->GetPage( 0 ) );
lastSchTextSimCommand = simTab->GetLastSchTextSimCommand(); js[ "last_sch_text_sim_command" ] = simTab->GetLastSchTextSimCommand();
} }
file.AddLine( lastSchTextSimCommand ); std::stringstream buffer;
buffer << std::setw( 2 ) << js << std::endl;
bool res = file.Write(); bool res = file.Write( buffer.str() );
file.Close(); file.Close();
// Store the filename of the last saved workbook. // Store the filename of the last saved workbook.

View File

@ -306,6 +306,9 @@ private:
void onCursorsGridCellChanged( wxGridEvent& aEvent ) override; void onCursorsGridCellChanged( wxGridEvent& aEvent ) override;
void onMeasurementsGridCellChanged( wxGridEvent& aEvent ) override; void onMeasurementsGridCellChanged( wxGridEvent& aEvent ) override;
bool loadLegacyWorkbook( const wxString & aPath );
bool loadJsonWorkbook( const wxString & aPath );
void onNotebookModified( wxCommandEvent& event ); void onNotebookModified( wxCommandEvent& event );
void onPlotCursorUpdate( wxCommandEvent& aEvent ); void onPlotCursorUpdate( wxCommandEvent& aEvent );