diff --git a/eeschema/dialogs/dialog_sim_model.cpp b/eeschema/dialogs/dialog_sim_model.cpp index 5e45705287..cee576e71b 100644 --- a/eeschema/dialogs/dialog_sim_model.cpp +++ b/eeschema/dialogs/dialog_sim_model.cpp @@ -140,6 +140,9 @@ bool DIALOG_SIM_MODEL::TransferDataToWindow() wxString pinMap; bool storeInValue = false; + wxString msg; + WX_STRING_REPORTER reporter( &msg ); + // Infer RLC and VI models if they aren't specified if( SIM_MODEL::InferSimModel( m_symbol, &m_fields, false, SIM_VALUE_GRAMMAR::NOTATION::SI, &deviceType, &modelType, &modelParams, &pinMap ) ) @@ -184,7 +187,7 @@ bool DIALOG_SIM_MODEL::TransferDataToWindow() if( !loadLibrary( libraryFilename ) ) { m_libraryPathText->ChangeValue( libraryFilename ); - m_curModelType = SIM_MODEL::ReadTypeFromFields( m_fields ); + m_curModelType = SIM_MODEL::ReadTypeFromFields( m_fields, &reporter ); m_libraryModelsMgr.CreateModel( nullptr, sourcePins, m_fields ); m_modelNameChoice->Append( _( "" ) ); @@ -260,25 +263,32 @@ bool DIALOG_SIM_MODEL::TransferDataToWindow() { // The model is sourced from the instance. m_useInstanceModelRadioButton->SetValue( true ); - m_curModelType = SIM_MODEL::ReadTypeFromFields( m_fields ); + + msg.clear(); + m_curModelType = SIM_MODEL::ReadTypeFromFields( m_fields, &reporter ); + + if( reporter.HasMessage() ) + DisplayErrorMessage( this, msg ); } + m_builtinModelsMgr.SetReporter( &reporter ); + for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() ) { - wxString msg; - WX_STRING_REPORTER reporter( &msg ); - - m_builtinModelsMgr.SetReporter( &reporter ); - if( m_useInstanceModelRadioButton->GetValue() && type == m_curModelType ) - m_builtinModelsMgr.CreateModel( m_fields, sourcePins, false ); - else - m_builtinModelsMgr.CreateModel( type, sourcePins ); - - if( reporter.HasMessage() ) { - DisplayErrorMessage( this, _( "Failed to read simulation model from fields." ) - + wxT( "\n\n" ) + msg ); + msg.clear(); + m_builtinModelsMgr.CreateModel( m_fields, sourcePins, false ); + + if( reporter.HasMessage() ) + { + DisplayErrorMessage( this, _( "Failed to read simulation model from fields." ) + + wxT( "\n\n" ) + msg ); + } + } + else + { + m_builtinModelsMgr.CreateModel( type, sourcePins ); } SIM_MODEL::DEVICE_T deviceTypeT = SIM_MODEL::TypeInfo( type ).deviceType; diff --git a/eeschema/netlist_exporters/netlist_exporter_spice.cpp b/eeschema/netlist_exporters/netlist_exporter_spice.cpp index 1c3b11703e..ba3f6ba436 100644 --- a/eeschema/netlist_exporters/netlist_exporter_spice.cpp +++ b/eeschema/netlist_exporters/netlist_exporter_spice.cpp @@ -83,20 +83,13 @@ namespace NETLIST_EXPORTER_SPICE_PARSER std::string NAME_GENERATOR::Generate( const std::string& aProposedName ) { - if( !m_names.count( aProposedName ) ) - return aProposedName; + std::string name = aProposedName; + int ii = 1; - for( uint64_t i = 1; i < UINT64_MAX; ++i ) - { - std::string name = fmt::format( "{}#{}", aProposedName, i ); + while( m_names.count( name ) ) + name = fmt::format( "{}#{}", aProposedName, ii++ ); - if( !m_names.count( name ) ) - return name; - } - - // Should never happen. - THROW_IO_ERROR( wxString::Format( _( "Failed to generate a name for '%s': exceeded UINT64_MAX" ), - aProposedName ) ); + return name; } @@ -126,8 +119,7 @@ bool NETLIST_EXPORTER_SPICE::DoWriteNetlist( OUTPUTFORMATTER& aFormatter, unsign // Cleanup list to avoid duplicate if the netlist exporter is run more than once. m_rawIncludes.clear(); - if( !ReadSchematicAndLibraries( aNetlistOptions, aReporter ) ) - return false; + bool result = ReadSchematicAndLibraries( aNetlistOptions, aReporter ); WriteHead( aFormatter, aNetlistOptions ); @@ -142,7 +134,7 @@ bool NETLIST_EXPORTER_SPICE::DoWriteNetlist( OUTPUTFORMATTER& aFormatter, unsign WriteTail( aFormatter, aNetlistOptions ); - return true; + return result; } @@ -165,6 +157,8 @@ bool NETLIST_EXPORTER_SPICE::ReadSchematicAndLibraries( unsigned aNetlistOptions std::set refNames; // Set of reference names to check for duplication. int ncCounter = 1; + m_libMgr.SetReporter( &aReporter ); + ReadDirectives( aNetlistOptions ); m_nets.clear(); @@ -240,27 +234,19 @@ bool NETLIST_EXPORTER_SPICE::ReadSchematicAndLibraries( unsigned aNetlistOptions wxString modelParams; wxString pinMap; - try - { - readRefName( sheet, *symbol, spiceItem, refNames ); - readModel( sheet, *symbol, spiceItem ); - readPinNumbers( *symbol, spiceItem, pins ); - readPinNetNames( *symbol, spiceItem, pins, ncCounter ); + readRefName( sheet, *symbol, spiceItem, refNames ); + readModel( sheet, *symbol, spiceItem ); + readPinNumbers( *symbol, spiceItem, pins ); + readPinNetNames( *symbol, spiceItem, pins, ncCounter ); - // TODO: transmission line handling? + // TODO: transmission line handling? - m_items.push_back( std::move( spiceItem ) ); - } - catch( const IO_ERROR& e ) - { - msg.Printf( _( "Error reading simulation model from symbol '%s':\n%s" ), - symbol->GetRef( &sheet ), - e.Problem() ); - aReporter.Report( msg, RPT_SEVERITY_ERROR ); - } + m_items.push_back( std::move( spiceItem ) ); } } + m_libMgr.SetReporter( nullptr ); + return !aReporter.HasMessage(); } diff --git a/eeschema/sim/sim_library_kibis.cpp b/eeschema/sim/sim_library_kibis.cpp index e8b0dc82b5..3c9b4b7155 100644 --- a/eeschema/sim/sim_library_kibis.cpp +++ b/eeschema/sim/sim_library_kibis.cpp @@ -49,7 +49,7 @@ void SIM_LIBRARY_KIBIS::ReadFile( const std::string& aFilePath, REPORTER* aRepor for( KIBIS_COMPONENT& kcomp : m_kibis.m_components ) { - m_models.push_back( SIM_MODEL::Create( SIM_MODEL::TYPE::KIBIS_DEVICE, pins, nullptr ) ); + m_models.push_back( SIM_MODEL::Create( SIM_MODEL::TYPE::KIBIS_DEVICE, pins, aReporter ) ); m_modelNames.emplace_back( kcomp.m_name ); SIM_MODEL_KIBIS* libcomp = dynamic_cast( m_models.back().get() ); diff --git a/eeschema/sim/sim_model.cpp b/eeschema/sim/sim_model.cpp index d1f4300ab3..cf007e07ee 100644 --- a/eeschema/sim/sim_model.cpp +++ b/eeschema/sim/sim_model.cpp @@ -361,11 +361,13 @@ SIM_MODEL::SPICE_INFO SIM_MODEL::SpiceInfo( TYPE aType ) } -template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ); -template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ); +template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields, + REPORTER* aReporter ); +template TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields, + REPORTER* aReporter ); template -TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ) +TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields, REPORTER* aReporter ) { std::string deviceTypeFieldValue = GetFieldValue( &aFields, SIM_DEVICE_TYPE_FIELD ); std::string typeFieldValue = GetFieldValue( &aFields, SIM_TYPE_FIELD ); @@ -392,6 +394,22 @@ TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields ) if( typeFromLegacyFields != TYPE::NONE ) return typeFromLegacyFields; + if( aReporter ) + { + if( aFields.size() > REFERENCE_FIELD ) + { + aReporter->Report( wxString::Format( _( "No simulation model definition found for " + "symbol '%s'." ), + aFields[REFERENCE_FIELD].GetText() ), + RPT_SEVERITY_ERROR ); + } + else + { + aReporter->Report( _( "No simulation model definition found." ), + RPT_SEVERITY_ERROR ); + } + } + return TYPE::NONE; } @@ -455,10 +473,7 @@ std::unique_ptr SIM_MODEL::Create( TYPE aType, const std::vectorReport( err.What(), RPT_SEVERITY_ERROR ); - else - DisplayErrorMessage( nullptr, err.What() ); + wxFAIL_MSG( "Shouldn't throw reading empty fields!" ); } return model; @@ -481,19 +496,16 @@ std::unique_ptr SIM_MODEL::Create( const SIM_MODEL* aBaseModel, else model = Create( type ); + if( aBaseModel ) + model->SetBaseModel( *aBaseModel ); + try { - if( aBaseModel ) - model->SetBaseModel( *aBaseModel ); - model->ReadDataFields( static_cast*>( nullptr ), aPins ); } catch( IO_ERROR& err ) { - if( aReporter ) - aReporter->Report( err.What(), RPT_SEVERITY_ERROR ); - else - DisplayErrorMessage( nullptr, err.What() ); + wxFAIL_MSG( "Shouldn't throw reading empty fields!" ); } return model; @@ -506,7 +518,7 @@ std::unique_ptr SIM_MODEL::Create( const SIM_MODEL* aBaseModel, const std::vector& aFields, REPORTER* aReporter ) { - TYPE type = ReadTypeFromFields( aFields ); + TYPE type = ReadTypeFromFields( aFields, aReporter ); // If the model has a specified type, it takes priority over the type of its base class. if( type == TYPE::NONE && aBaseModel ) @@ -523,19 +535,23 @@ std::unique_ptr SIM_MODEL::Create( const SIM_MODEL* aBaseModel, else model = Create( type ); + if( aBaseModel ) + model->SetBaseModel( *aBaseModel ); + try { - if( aBaseModel ) - model->SetBaseModel( *aBaseModel ); - model->ReadDataFields( &aFields, aPins ); } catch( IO_ERROR& err ) { if( aReporter ) - aReporter->Report( err.What(), RPT_SEVERITY_ERROR ); - else - DisplayErrorMessage( nullptr, err.What() ); + { + aReporter->Report( wxString::Format( _( "Error reading simulation model from " + "symbol '%s':\n%s" ), + aFields[REFERENCE_FIELD].GetText(), + err.Problem() ), + RPT_SEVERITY_ERROR ); + } } return model; @@ -557,7 +573,7 @@ std::unique_ptr SIM_MODEL::Create( const std::vector& aFields, const std::vector& aPins, bool aResolved, REPORTER* aReporter ) { - TYPE type = ReadTypeFromFields( aFields ); + TYPE type = ReadTypeFromFields( aFields, aReporter ); std::unique_ptr model = SIM_MODEL::Create( type ); try @@ -589,11 +605,15 @@ std::unique_ptr SIM_MODEL::Create( const std::vector& aFields, } catch( const IO_ERROR& err ) { - // We own the pin syntax, so if we can't parse it then there's an error, full stop. + // We own the pin syntax, so if we can't parse it then there's an error. if( aReporter ) - aReporter->Report( err.Problem(), RPT_SEVERITY_ERROR ); - else - THROW_IO_ERROR( err.Problem() ); + { + aReporter->Report( wxString::Format( _( "Error reading simulation model from " + "symbol '%s':\n%s" ), + aFields[REFERENCE_FIELD].GetText(), + err.Problem() ), + RPT_SEVERITY_ERROR ); + } } } @@ -726,21 +746,8 @@ void SIM_MODEL::AddParam( const PARAM::INFO& aInfo ) void SIM_MODEL::SetBaseModel( const SIM_MODEL& aBaseModel ) { - auto describe = - []( const SIM_MODEL* aModel ) - { - return fmt::format( "{} ({})", - aModel->GetDeviceInfo().fieldValue, - aModel->GetTypeInfo().description ); - }; - - if( GetType() != aBaseModel.GetType() ) - { - THROW_IO_ERROR( wxString::Format( _( "Simulation model type must be the same as of its " - "base class: '%s', but is '%s'" ), - describe( &aBaseModel ), - describe( this ) ) ); - } + wxASSERT_MSG( GetType() == aBaseModel.GetType(), + wxS( "Simulation model type must be the same as its base class!" ) ); m_baseModel = &aBaseModel; } diff --git a/eeschema/sim/sim_model.h b/eeschema/sim/sim_model.h index 899aebff9e..8b299981e8 100644 --- a/eeschema/sim/sim_model.h +++ b/eeschema/sim/sim_model.h @@ -384,7 +384,7 @@ public: template - static TYPE ReadTypeFromFields( const std::vector& aFields ); + static TYPE ReadTypeFromFields( const std::vector& aFields, REPORTER* aReporter ); template static TYPE InferTypeFromLegacyFields( const std::vector& aFields ); diff --git a/eeschema/sim/sim_model_ngspice.cpp b/eeschema/sim/sim_model_ngspice.cpp index 66b35297e5..801f0961a5 100644 --- a/eeschema/sim/sim_model_ngspice.cpp +++ b/eeschema/sim/sim_model_ngspice.cpp @@ -55,7 +55,6 @@ std::vector SPICE_GENERATOR_NGSPICE::CurrentNames( const SPICE_ITEM return SPICE_GENERATOR::CurrentNames( aItem ); default: - wxFAIL_MSG( "Unhandled model device type in SIM_MODEL_NGSPICE" ); return {}; } } diff --git a/eeschema/sim/spice_generator.cpp b/eeschema/sim/spice_generator.cpp index 54d36be732..c719e55d1b 100644 --- a/eeschema/sim/spice_generator.cpp +++ b/eeschema/sim/spice_generator.cpp @@ -95,6 +95,10 @@ std::string SPICE_GENERATOR::ModelLine( const SPICE_ITEM& aItem ) const value ) ); } + // Don't send SPICE empty models. + if( result.length() == indentLength + 1 /* line ending */ ) + result.clear(); + return result; } diff --git a/eeschema/tools/simulator_control.cpp b/eeschema/tools/simulator_control.cpp index 87c575c445..9c62ca549a 100644 --- a/eeschema/tools/simulator_control.cpp +++ b/eeschema/tools/simulator_control.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -350,45 +351,74 @@ public: wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL ) ); } - NETLIST_VIEW_DIALOG( wxWindow* parent, const wxString& source) : + NETLIST_VIEW_DIALOG( wxWindow* parent ) : DIALOG_SHIM( parent, wxID_ANY, _( "SPICE Netlist" ), wxDefaultPosition, - wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ) + wxSize( 800, 800 ), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ), + m_textCtrl( nullptr ), + m_reporter( nullptr ) { - wxStyledTextCtrl* textCtrl = new wxStyledTextCtrl( this, wxID_ANY ); - textCtrl->SetMinSize( wxSize( 600, 400 ) ); + m_splitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH ); - textCtrl->SetMarginWidth( MARGIN_LINE_NUMBERS, 50 ); - textCtrl->StyleSetForeground( wxSTC_STYLE_LINENUMBER, wxColour( 75, 75, 75 ) ); - textCtrl->StyleSetBackground( wxSTC_STYLE_LINENUMBER, wxColour( 220, 220, 220 ) ); - textCtrl->SetMarginType( MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER ); + //Avoid the splitter window being assigned as the Parent to additional windows + m_splitter->SetExtraStyle( wxWS_EX_TRANSIENT ); + + m_textCtrl = new wxStyledTextCtrl( m_splitter, wxID_ANY ); + + m_textCtrl->SetMarginWidth( MARGIN_LINE_NUMBERS, 50 ); + m_textCtrl->StyleSetForeground( wxSTC_STYLE_LINENUMBER, wxColour( 75, 75, 75 ) ); + m_textCtrl->StyleSetBackground( wxSTC_STYLE_LINENUMBER, wxColour( 220, 220, 220 ) ); + m_textCtrl->SetMarginType( MARGIN_LINE_NUMBERS, wxSTC_MARGIN_NUMBER ); wxFont fixedFont = KIUI::GetMonospacedUIFont(); for( int i = 0; i < wxSTC_STYLE_MAX; ++i ) - textCtrl->StyleSetFont( i, fixedFont ); + m_textCtrl->StyleSetFont( i, fixedFont ); - textCtrl->StyleClearAll(); // Addresses a bug in wx3.0 where styles are not correctly set + m_textCtrl->StyleClearAll(); // Addresses a bug in wx3.0 where styles are not correctly set - textCtrl->SetWrapMode( wxSTC_WRAP_WORD ); + m_textCtrl->SetWrapMode( wxSTC_WRAP_WORD ); + m_textCtrl->SetLexer( wxSTC_LEX_SPICE ); + m_textCtrl->SetMinSize( wxSize( 40, 40 ) ); + m_textCtrl->SetSize( wxSize( 40, 40 ) ); - textCtrl->SetText( source ); + m_reporter = new WX_HTML_REPORT_BOX( m_splitter, wxID_ANY ); + m_reporter->SetMinSize( wxSize( 40, 40 ) ); + m_reporter->SetSize( wxSize( 40, 40 ) ); - textCtrl->SetLexer( wxSTC_LEX_SPICE ); - - textCtrl->SetEditable( false ); + m_splitter->SetMinimumPaneSize( 40 ); + m_splitter->SetSashPosition( 760 ); + m_splitter->SetSashGravity( 0.9 ); + m_splitter->SplitHorizontally( m_textCtrl, m_reporter ); wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); - sizer->Add( textCtrl, 1, wxEXPAND | wxALL, 5 ); + sizer->Add( m_splitter, 1, wxEXPAND | wxALL, 5 ); SetSizer( sizer ); + Layout(); Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( NETLIST_VIEW_DIALOG::onClose ), nullptr, this ); - m_scintillaTricks = std::make_unique( textCtrl, wxT( "{}" ), false ); + m_scintillaTricks = std::make_unique( m_textCtrl, wxT( "{}" ), false ); finishDialogSettings(); } + void SetNetlist( const wxString& aSource ) + { + m_textCtrl->SetText( aSource ); + m_textCtrl->SetEditable( false ); + + m_reporter->Flush(); + } + + REPORTER* GetReporter() { return m_reporter; } + +private: + wxSplitterWindow* m_splitter; + wxStyledTextCtrl* m_textCtrl; + WX_HTML_REPORT_BOX* m_reporter; + std::unique_ptr m_scintillaTricks; }; @@ -411,14 +441,13 @@ int SIMULATOR_CONTROL::ShowNetlist( const TOOL_EVENT& aEvent ) if( m_schematicFrame == nullptr || m_simulator == nullptr ) return -1; - wxString errors; - WX_STRING_REPORTER reporter( &errors ); - STRING_FORMATTER formatter; + STRING_FORMATTER formatter; + NETLIST_VIEW_DIALOG dlg( m_simulatorFrame ); m_circuitModel->SetSimOptions( m_simulatorFrame->GetCurrentOptions() ); - m_circuitModel->GetNetlist( &formatter, reporter ); + m_circuitModel->GetNetlist( &formatter, *dlg.GetReporter() ); - NETLIST_VIEW_DIALOG dlg( m_simulatorFrame, errors.IsEmpty() ? wxString( formatter.GetString() ) : errors ); + dlg.SetNetlist( wxString( formatter.GetString() ) ); dlg.ShowModal(); return 0;