KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_sim_model.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2022 Mikolaj Wielgus
5 * Copyright (C) 2022 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <bitmaps.h>
24#include <dialog_sim_model.h>
25#include <sim/sim_property.h>
27#include <sim/sim_model.h>
29#include <sim/sim_model_ibis.h>
33#include <grid_tricks.h>
36#include <kiplatform/ui.h>
37#include <confirm.h>
38#include <string_utils.h>
39
40#include <wx/filedlg.h>
41#include <fmt/format.h>
42#include <sch_edit_frame.h>
43#include <widgets/wx_infobar.h>
47#include <wx/filedlg.h>
48#include <wx/log.h>
49
51
52#define FORCE_REFRESH_FROM_MODEL true
53
54
55bool equivalent( SIM_MODEL::DEVICE_T a, SIM_MODEL::DEVICE_T b )
56{
57 // A helper to handle SPICE's use of 'E' and 'H' for voltage sources and 'F' and 'G' for
58 // current sources
59 return a == b
60 || SIM_MODEL::DeviceInfo( a ).description == SIM_MODEL::DeviceInfo( b ).description;
61};
62
63
64template <typename T>
65DIALOG_SIM_MODEL<T>::DIALOG_SIM_MODEL( wxWindow* aParent, EDA_BASE_FRAME* aFrame, T& aSymbol,
66 std::vector<SCH_FIELD>& aFields ) :
67 DIALOG_SIM_MODEL_BASE( aParent ),
68 m_frame( aFrame ),
69 m_symbol( aSymbol ),
70 m_fields( aFields ),
74 m_prevModel( nullptr ),
76 m_scintillaTricksCode( nullptr ),
77 m_scintillaTricksSubckt( nullptr ),
78 m_firstCategory( nullptr ),
79 m_prevParamGridSelection( nullptr ),
81{
83 m_infoBar->AddCloseButton();
84
85 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
86 {
87 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( &aSymbol );
88 m_filesStack.push_back( symbol->Schematic()->GetEmbeddedFiles() );
89 }
90
91 if( EMBEDDED_FILES* symbolEmbeddedFiles = aSymbol.GetEmbeddedFiles() )
92 {
93 m_filesStack.push_back( symbolEmbeddedFiles );
94
95 if constexpr (std::is_same_v<T, SCH_SYMBOL>)
96 {
97 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( &aSymbol );
98 symbol->GetLibSymbolRef()->AppendParentEmbeddedFiles( m_filesStack );
99 }
100 else if constexpr (std::is_same_v<T, LIB_SYMBOL>)
101 {
102 LIB_SYMBOL* symbol = static_cast<LIB_SYMBOL*>( &aSymbol );
104 }
105 }
106
107 m_libraryModelsMgr.SetFilesStack( m_filesStack );
108 m_builtinModelsMgr.SetFilesStack( m_filesStack );
109
110 for( SCH_PIN* pin : aSymbol.GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
111 {
112 // Body styles (including De Morgan variants) are equivalences, not additional items to simulate
113 if( !pin->GetParentSymbol()->IsMultiBodyStyle() || pin->GetBodyStyle() < 2 )
114 m_sortedPartPins.push_back( pin );
115 }
116
117 std::sort( m_sortedPartPins.begin(), m_sortedPartPins.end(),
118 []( const SCH_PIN* lhs, const SCH_PIN* rhs )
119 {
120 // We sort by StrNumCmp because SIM_MODEL_BASE sorts with it too.
121 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
122 } );
123
124 m_waveformChoice->Clear();
125 m_deviceChoice->Clear();
126 m_deviceSubtypeChoice->Clear();
127
128 m_scintillaTricksCode = new SCINTILLA_TRICKS( m_codePreview, wxT( "{}" ), false );
129 m_scintillaTricksSubckt = new SCINTILLA_TRICKS( m_subckt, wxT( "()" ), false );
130
131 m_paramGridMgr->Bind( wxEVT_PG_SELECTED, &DIALOG_SIM_MODEL::onParamGridSelectionChange, this );
132
133 wxPropertyGrid* grid = m_paramGrid->GetGrid();
134 grid->SetCellDisabledTextColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
135 grid->Bind( wxEVT_SET_FOCUS, &DIALOG_SIM_MODEL::onParamGridSetFocus, this );
136 grid->Bind( wxEVT_UPDATE_UI, &DIALOG_SIM_MODEL::onUpdateUI, this );
137
138 grid->DedicateKey( WXK_RETURN );
139 grid->DedicateKey( WXK_NUMPAD_ENTER );
140 grid->DedicateKey( WXK_UP );
141 grid->DedicateKey( WXK_DOWN );
142
143#if wxCHECK_VERSION( 3, 3, 0 )
144 grid->AddActionTrigger( wxPGKeyboardAction::Edit, WXK_RETURN );
145 grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_RETURN );
146 grid->AddActionTrigger( wxPGKeyboardAction::Edit, WXK_NUMPAD_ENTER );
147 grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_NUMPAD_ENTER );
148#else
149 grid->AddActionTrigger( wxPG_ACTION_EDIT, WXK_RETURN );
150 grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RETURN );
151 grid->AddActionTrigger( wxPG_ACTION_EDIT, WXK_NUMPAD_ENTER );
152 grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_NUMPAD_ENTER );
153#endif
154
155 m_pinAssignmentsGrid->ClearRows();
156 m_pinAssignmentsGrid->PushEventHandler( new GRID_TRICKS( m_pinAssignmentsGrid ) );
157
158 m_subcktLabel->SetFont( KIUI::GetStatusFont( m_subcktLabel ) );
159 finishDialogSettings();
160}
161
162
163template <typename T>
165{
166 // Disable all properties. This is necessary because some of their methods are called after
167 // destruction of DIALOG_SIM_MODEL, oddly. When disabled, they never access their models.
168 for( wxPropertyGridIterator it = m_paramGrid->GetIterator(); !it.AtEnd(); ++it )
169 {
170 if( SIM_PROPERTY* prop = dynamic_cast<SIM_PROPERTY*>( *it ) )
171 prop->Disable();
172 }
173
174 // Delete the GRID_TRICKS.
175 m_pinAssignmentsGrid->PopEventHandler( true );
176
179}
180
181
182template <typename T>
184{
185 wxCommandEvent dummyEvent;
186 wxString deviceType;
187 wxString modelType;
188 wxString modelParams;
189 wxString pinMap;
190 bool storeInValue = false;
191
193
194 // Infer RLC and VI models if they aren't specified
196 &deviceType, &modelType, &modelParams, &pinMap ) )
197 {
198 SetFieldValue( m_fields, SIM_DEVICE_FIELD, deviceType.ToStdString() );
199
200 if( !modelType.IsEmpty() )
201 SetFieldValue( m_fields, SIM_DEVICE_SUBTYPE_FIELD, modelType.ToStdString() );
202
203 SetFieldValue( m_fields, SIM_PARAMS_FIELD, modelParams.ToStdString() );
204
205 SetFieldValue( m_fields, SIM_PINS_FIELD, pinMap.ToStdString() );
206
207 storeInValue = true;
208
209 // WriteFields() only rewrites Value when storeInValue stays on, so remember the original
210 // text first to restore it verbatim if the user turns storeInValue off.
211 SCH_FIELD* valueField = FindField( m_fields, FIELD_T::VALUE );
212
213 m_inferredValueRestore = valueField->GetText();
215 valueField->SetText( wxT( "${SIM.PARAMS}" ) );
216 }
217
218 wxString libraryFilename = GetFieldValue( &m_fields, SIM_LIBRARY::LIBRARY_FIELD, true, 0 );
219 wxFileName tmp( libraryFilename );
220
221 if( !tmp.GetFullName().IsEmpty() )
222 {
223 // The model is sourced from a library, optionally with instance overrides.
224 m_rbLibraryModel->SetValue( true );
225
226 if( !loadLibrary( libraryFilename, reporter ) )
227 {
228 if( reporter.HasMessage() )
229 m_infoBar->ShowMessage( reporter.GetMessages() );
230
231 m_libraryPathText->ChangeValue( libraryFilename );
233
234 m_libraryModelsMgr.CreateModel( nullptr, SIM_MODEL::PinNumbers( m_sortedPartPins ), m_fields, true, 0,
235 reporter );
236
237 m_modelListBox->Clear();
238 m_modelListBox->Append( _( "<unknown>" ) );
239 m_modelListBox->SetSelection( 0 );
240 }
241 else
242 {
243 std::string modelName = GetFieldValue( &m_fields, SIM_LIBRARY::NAME_FIELD, true, 0 );
244 int modelIdx = m_modelListBox->FindString( modelName );
245
246 if( modelIdx == wxNOT_FOUND )
247 {
248 m_infoBar->ShowMessage( wxString::Format( _( "No model named '%s' in library." ), modelName ) );
249
250 // Default to first item in library if any exist
251 if( m_modelListBox->GetCount() > 0 )
252 m_modelListBox->SetSelection( 0 );
253 }
254 else
255 {
256 m_infoBar->Hide();
257 m_modelListBox->SetSelection( modelIdx );
258 }
259
260 m_curModelType = curModel().GetType();
261 }
262
263 if( isIbisLoaded() && ( m_modelListBox->GetSelection() >= 0 ) )
264 {
265 int idx = 0;
266 wxString sel = m_modelListBox->GetStringSelection();
267
268 if( m_modelListBoxEntryToLibraryIdx.contains( sel ) )
269 idx = m_modelListBoxEntryToLibraryIdx.at( sel );
270
271 SIM_MODEL_IBIS* ibismodel = dynamic_cast<SIM_MODEL_IBIS*>( &m_libraryModelsMgr.GetModels()[idx].get() );
272
273 if( ibismodel )
274 {
275 onModelNameChoice( dummyEvent ); // refresh list of pins
276
277 int i = 0;
278
279 for( const std::pair<std::string, std::string>& strs : ibismodel->GetIbisPins() )
280 {
281 if( strs.first == GetFieldValue( &m_fields, SIM_LIBRARY_IBIS::PIN_FIELD, true, 0 ) )
282 {
283 auto ibisLibrary = static_cast<const SIM_LIBRARY_IBIS*>( library() );
284
285 ibismodel->ChangePin( *ibisLibrary, strs.first );
286 m_pinCombobox->SetSelection( static_cast<int>( i ) );
287 break;
288 }
289 i++;
290 }
291
292 if( i < static_cast<int>( ibismodel->GetIbisPins().size() ) )
293 {
294 onPinCombobox( dummyEvent ); // refresh list of models
295
296 wxString ibisModel = GetFieldValue( &m_fields, SIM_LIBRARY_IBIS::MODEL_FIELD, true, 0 );
297 m_pinModelCombobox->SetStringSelection( ibisModel );
298 }
299
300 if( GetFieldValue( &m_fields, SIM_LIBRARY_IBIS::DIFF_FIELD, true, 0 ) == "1" )
301 {
302 ibismodel->SwitchSingleEndedDiff( true );
303 m_differentialCheckbox->SetValue( true );
304 }
305 else
306 {
307 ibismodel->SwitchSingleEndedDiff( false );
308 m_differentialCheckbox->SetValue( false );
309 }
310
311 // SetIbisModel derives the IO mode from KIBIS_MODEL::m_type
312 // (Series / Series_switch override the differential layout).
313 auto ibisLibrary = static_cast<const SIM_LIBRARY_IBIS*>( library() );
314 std::string pinNum = GetFieldValue( &m_fields, SIM_LIBRARY_IBIS::PIN_FIELD,
315 true, 0 );
317 true, 0 );
318
319 if( ibisLibrary && !pinNum.empty() && !modelNm.empty() )
320 ibismodel->SetIbisModel( *ibisLibrary, pinNum, modelNm );
321 }
322 }
323 }
324 else if( !GetFieldValue( &m_fields, SIM_DEVICE_FIELD, false, 0 ).empty()
326 {
327 // The model is sourced from the instance.
328 m_rbBuiltinModel->SetValue( true );
329
330 reporter.Clear();
332
333 if( reporter.HasMessage() )
334 DisplayErrorMessage( this, reporter.GetMessages() );
335 }
336
337 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
338 {
339 if( m_rbBuiltinModel->GetValue() && type == m_curModelType )
340 {
341 reporter.Clear();
343
344 if( reporter.HasMessage() )
345 {
346 DisplayErrorMessage( this, _( "Failed to read simulation model from fields." )
347 + wxT( "\n\n" ) + reporter.GetMessages() );
348 }
349 }
350 else
351 {
353 }
354
355 SIM_MODEL::DEVICE_T deviceTypeT = SIM_MODEL::TypeInfo( type ).deviceType;
356
357 if( !m_curModelTypeOfDeviceType.count( deviceTypeT ) )
358 m_curModelTypeOfDeviceType[deviceTypeT] = type;
359 }
360
361 if( storeInValue )
362 curModel().SetIsStoredInValue( true );
363
364 m_saveInValueCheckbox->SetValue( curModel().IsStoredInValue() );
365
367
368 onRadioButton( dummyEvent );
369 return DIALOG_SIM_MODEL_BASE::TransferDataToWindow();
370}
371
372
373template <typename T>
375{
376 if( !m_pinAssignmentsGrid->CommitPendingChanges() )
377 return false;
378
379 if( !m_paramGrid->GetGrid()->CommitChangesFromEditor() )
380 return false;
381
382 if( !DIALOG_SIM_MODEL_BASE::TransferDataFromWindow() )
383 return false;
384
386 std::string path;
387 std::string name;
388
389 if( m_rbLibraryModel->GetValue() )
390 {
391 path = m_libraryPathText->GetValue();
392 wxFileName fn( path );
393
394 if( !path.starts_with( FILEEXT::KiCadUriPrefix ) && fn.MakeRelativeTo( Prj().GetProjectPath() )
395 && !fn.GetFullPath().StartsWith( ".." ) )
396 {
397 path = fn.GetFullPath();
398 }
399
400 if( m_modelListBox->GetSelection() >= 0 )
401 name = m_modelListBox->GetStringSelection().ToStdString();
402 else if( dynamic_cast<SIM_MODEL_SPICE_FALLBACK*>( &model ) )
404 }
405
408
409 if( isIbisLoaded() && !m_libraryModelsMgr.GetModels().empty() )
410 {
411 int idx = 0;
412 wxString sel = m_modelListBox->GetStringSelection();
413
414 if( m_modelListBoxEntryToLibraryIdx.contains( sel ) )
415 idx = m_modelListBoxEntryToLibraryIdx.at( sel );
416
417 auto* ibismodel =
418 static_cast<SIM_MODEL_IBIS*>( &m_libraryModelsMgr.GetModels().at( idx ).get() );
419
420 if( ibismodel )
421 {
422 std::string pins;
423 std::string modelName = std::string( m_pinModelCombobox->GetValue().c_str() );
424 std::string differential;
425
426 if( m_pinCombobox->GetSelection() >= 0 )
427 pins = ibismodel->GetIbisPins().at( m_pinCombobox->GetSelection() ).first;
428
429 if( ibismodel->CanDifferential() && m_differentialCheckbox->GetValue() )
430 differential = "1";
431
435 }
436 }
437
438 if( model.GetType() == SIM_MODEL::TYPE::RAWSPICE )
439 {
440 if( m_modelNotebook->GetSelection() == 0 )
442
443 wxString code = m_codePreview->GetText().Trim( true ).Trim( false );
444 model.SetParamValue( "model", std::string( code.ToUTF8() ) );
445 }
446
447 model.SetIsStoredInValue( m_saveInValueCheckbox->GetValue() );
448
449 for( int row = 0; row < m_pinAssignmentsGrid->GetNumberRows(); ++row )
450 {
451 wxString modelPinName = m_pinAssignmentsGrid->GetCellValue( row, PIN_COLUMN::MODEL );
452 wxString symbolPinName = m_sortedPartPins.at( row )->GetShownNumber();
453
454 model.AssignSymbolPinNumberToModelPin( getModelPinIndex( modelPinName ),
455 std::string( symbolPinName.ToUTF8() ) );
456 }
457
459
460 curModel().WriteFields( m_fields );
461
463 model.IsStoredInValue() );
464
465 // Decomposition is a component-level choice and is independent of the model fields written
466 // above. Only multi-unit symbols expose it; write it after WriteFields() so it is preserved.
467 if( m_symbol.GetUnitCount() > 1 )
468 {
469 SIM_DECOMPOSITION decomposition;
470
471 if( m_decompositionChoice->GetSelection() == 1 )
472 {
474
475 wxStringTokenizer sharedTokens( m_sharedPinsText->GetValue(), wxS( ", \t" ),
476 wxTOKEN_STRTOK );
477
478 while( sharedTokens.HasMoreTokens() )
479 decomposition.sharedModelPins.push_back( sharedTokens.GetNextToken() );
480 }
481
482 // Whole-device formats to an empty string, which clears any previous repeat selection.
483 SetFieldValue( m_fields, SIM_DECOMPOSITION_FIELD, decomposition.Format().ToStdString(),
484 false );
485 }
486
487 return true;
488}
489
490
491template <typename T>
492void DIALOG_SIM_MODEL<T>::RestoreInferredValue( std::vector<SCH_FIELD>& aFields,
493 const wxString& aOriginalValue, bool aOverwritten,
494 bool aStoredInValue )
495{
496 if( !aOverwritten || aStoredInValue )
497 return;
498
499 if( SCH_FIELD* valueField = FindField( aFields, FIELD_T::VALUE ) )
500 valueField->SetText( aOriginalValue );
501}
502
503
504template <typename T>
506{
507 bool multiUnit = m_symbol.GetUnitCount() > 1;
508
509 m_decompositionLabel->Show( multiUnit );
510 m_decompositionChoice->Show( multiUnit );
511 m_sharedPinsLabel->Show( multiUnit );
512 m_sharedPinsText->Show( multiUnit );
513
514 if( multiUnit )
515 {
518
519 bool repeat = decomposition.mode == SIM_DECOMPOSITION::MODE::REPEAT_PER_UNIT;
520
521 m_decompositionChoice->SetSelection( repeat ? 1 : 0 );
522
523 wxString shared;
524
525 for( const wxString& pin : decomposition.sharedModelPins )
526 {
527 if( !shared.IsEmpty() )
528 shared += wxS( ", " );
529
530 shared += pin;
531 }
532
533 m_sharedPinsText->SetValue( shared );
534 m_sharedPinsLabel->Enable( repeat );
535 m_sharedPinsText->Enable( repeat );
536 }
537
538 m_pinAssignmentsPanel->Layout();
539}
540
541
542template <typename T>
544{
545 bool repeat = m_decompositionChoice->GetSelection() == 1;
546
547 m_sharedPinsLabel->Enable( repeat );
548 m_sharedPinsText->Enable( repeat );
549
550 aEvent.Skip();
551}
552
553
554template <typename T>
556{
557 // always enable the library browser button -- it makes for fewer clicks if the user has a
558 // whole bunch of inferred passives that they want to specify library models for
559 m_browseButton->Enable();
560
561 // if we're in an undetermined state then enable everything for faster access
562 bool undetermined = !m_rbLibraryModel->GetValue() && !m_rbBuiltinModel->GetValue();
563 bool enableLibCtrls = m_rbLibraryModel->GetValue() || undetermined;
564 bool enableBuiltinCtrls = m_rbBuiltinModel->GetValue() || undetermined;
565
566 m_pathLabel->Enable( enableLibCtrls );
567 m_libraryPathText->Enable( enableLibCtrls );
568 m_modelNameLabel->Enable( enableLibCtrls );
569 m_modelFilter->Enable( enableLibCtrls && !isIbisLoaded() );
570 m_modelListBox->Enable( enableLibCtrls );
571 m_pinLabel->Enable( enableLibCtrls );
572 m_pinCombobox->Enable( enableLibCtrls );
573 m_differentialCheckbox->Enable( enableLibCtrls );
574 m_pinModelLabel->Enable( enableLibCtrls );
575 m_pinModelCombobox->Enable( enableLibCtrls );
576 m_waveformLabel->Enable( enableLibCtrls );
577 m_waveformChoice->Enable( enableLibCtrls );
578
579 m_deviceLabel->Enable( enableBuiltinCtrls );
580 m_deviceChoice->Enable( enableBuiltinCtrls );
581 m_deviceSubtypeLabel->Enable( enableBuiltinCtrls );
582 m_deviceSubtypeChoice->Enable( enableBuiltinCtrls );
583
585
590 updatePinAssignments( model, false );
591
592 std::string ref = GetFieldValue( &m_fields, SIM_REFERENCE_FIELD, false, 0 );
593
594 m_modelPanel->Layout();
595 m_pinAssignmentsPanel->Layout();
596 m_parametersPanel->Layout();
597 m_codePanel->Layout();
598
599 SendSizeEvent( wxSEND_EVENT_POST );
600
602}
603
604
605template <typename T>
607{
608 SIM_MODEL_IBIS* modelibis = isIbisLoaded() ? dynamic_cast<SIM_MODEL_IBIS*>( aModel ) : nullptr;
609
610 m_pinLabel->Show( isIbisLoaded() );
611 m_pinCombobox->Show( isIbisLoaded() );
612 m_pinModelLabel->Show( isIbisLoaded() );
614 m_waveformLabel->Show( isIbisLoaded() );
616
617 if( aModel != m_prevModel )
618 {
619 m_waveformChoice->Clear();
620
621 if( isIbisLoaded() )
622 {
623 for( SIM_MODEL::TYPE type : { SIM_MODEL::TYPE::KIBIS_DEVICE,
624 SIM_MODEL::TYPE::KIBIS_DRIVER_DC,
625 SIM_MODEL::TYPE::KIBIS_DRIVER_RECT,
626 SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS } )
627 {
628 SIM_MODEL::DEVICE_T deviceType = SIM_MODEL::TypeInfo( type ).deviceType;
629 const std::string& deviceTypeDesc = SIM_MODEL::DeviceInfo( deviceType ).description;
630
631 if( deviceType == aModel->GetDeviceType()
632 || deviceTypeDesc == aModel->GetDeviceInfo().description )
633 {
634 m_waveformChoice->Append( SIM_MODEL::TypeInfo( type ).description );
635
636 if( type == aModel->GetType() )
637 m_waveformChoice->SetSelection( m_waveformChoice->GetCount() - 1 );
638 }
639 }
640 }
641 }
642
643 m_differentialCheckbox->Show( isIbisLoaded() && modelibis && modelibis->CanDifferential() );
644 m_modelNameLabel->SetLabel( isIbisLoaded() ? _( "Component:" ) : _( "Model:" ) );
645}
646
647
648template <typename T>
650{
651 // Change the Type choice to match the current device type.
652 if( aModel != m_prevModel )
653 {
654 m_deviceChoice->Clear();
655 m_deviceSubtypeChoice->Clear();
656
657 if( !m_rbLibraryModel->GetValue() )
658 {
659 for( SIM_MODEL::DEVICE_T deviceType : SIM_MODEL::DEVICE_T_ITERATOR() )
660 {
661 if( !SIM_MODEL::DeviceInfo( deviceType ).showInMenu )
662 continue;
663
664 m_deviceChoice->Append( SIM_MODEL::DeviceInfo( deviceType ).description );
665
666 if( equivalent( deviceType, aModel->GetDeviceType() ) )
667 m_deviceChoice->SetSelection( m_deviceChoice->GetCount() - 1 );
668 }
669
670 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
671 {
672 if( type == SIM_MODEL::TYPE::KIBIS_DEVICE
673 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_DC
674 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_RECT
675 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS )
676 {
677 continue;
678 }
679
680 SIM_MODEL::DEVICE_T deviceType = SIM_MODEL::TypeInfo( type ).deviceType;
681 const std::string& deviceTypeDesc = SIM_MODEL::DeviceInfo( deviceType ).description;
682
683 if( deviceType == aModel->GetDeviceType()
684 || deviceTypeDesc == aModel->GetDeviceInfo().description )
685 {
686 m_deviceSubtypeChoice->Append( SIM_MODEL::TypeInfo( type ).description );
687
688 if( type == aModel->GetType() )
689 m_deviceSubtypeChoice->SetSelection( m_deviceSubtypeChoice->GetCount() - 1 );
690 }
691 }
692 }
693
694 m_deviceSubtypeLabel->Show( m_deviceSubtypeChoice->GetCount() > 1 );
695 m_deviceSubtypeChoice->Show( m_deviceSubtypeChoice->GetCount() > 1 );
696 }
697
698 if( dynamic_cast<SIM_MODEL_RAW_SPICE*>( aModel ) )
699 m_modelNotebook->SetSelection( 1 );
700 else
701 m_modelNotebook->SetSelection( 0 );
702
703 if( aModel->HasPrimaryValue() )
704 {
705 const SIM_MODEL::PARAM& primary = aModel->GetParam( 0 );
706
707 m_saveInValueCheckbox->SetLabel( wxString::Format( _( "Save parameter '%s (%s)' in Value "
708 "field" ),
709 primary.info.description,
710 primary.info.name ) );
711 m_saveInValueCheckbox->Enable( true );
712 }
713 else
714 {
715 m_saveInValueCheckbox->SetLabel( _( "Save primary parameter in Value field" ) );
716 m_saveInValueCheckbox->SetValue( false );
717 m_saveInValueCheckbox->Enable( false );
718 }
719}
720
721
722template <typename T>
724{
725 if( aModel != m_prevModel )
726 {
727 // This wxPropertyGridManager column and header stuff has to be here because it segfaults in
728 // the constructor.
729
730 m_paramGridMgr->SetColumnCount( PARAM_COLUMN::END_ );
731
732 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::DESCRIPTION, _( "Parameter" ) );
733 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::UNIT, _( "Unit" ) );
734 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::DEFAULT, _( "Default" ) );
735 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::TYPE, _( "Type" ) );
736
737 m_paramGridMgr->ShowHeader();
738
739
740 m_paramGrid->Clear();
741
742 m_firstCategory = m_paramGrid->Append( new wxPropertyCategory( "Geometry" ) );
743 m_paramGrid->HideProperty( "Geometry" );
744
745 m_paramGrid->Append( new wxPropertyCategory( "AC" ) );
746 m_paramGrid->HideProperty( "AC" );
747
748 m_paramGrid->Append( new wxPropertyCategory( "DC" ) );
749 m_paramGrid->HideProperty( "DC" );
750
751 m_paramGrid->Append( new wxPropertyCategory( "S-Parameters" ) );
752 m_paramGrid->HideProperty( "S-Parameters" );
753
754 m_paramGrid->Append( new wxPropertyCategory( "Capacitance" ) );
755 m_paramGrid->HideProperty( "Capacitance" );
756
757 m_paramGrid->Append( new wxPropertyCategory( "Temperature" ) );
758 m_paramGrid->HideProperty( "Temperature" );
759
760 m_paramGrid->Append( new wxPropertyCategory( "Noise" ) );
761 m_paramGrid->HideProperty( "Noise" );
762
763 m_paramGrid->Append( new wxPropertyCategory( "Distributed Quantities" ) );
764 m_paramGrid->HideProperty( "Distributed Quantities" );
765
766 m_paramGrid->Append( new wxPropertyCategory( "Waveform" ) );
767 m_paramGrid->HideProperty( "Waveform" );
768
769 m_paramGrid->Append( new wxPropertyCategory( "Limiting Values" ) );
770 m_paramGrid->HideProperty( "Limiting Values" );
771
772 m_paramGrid->Append( new wxPropertyCategory( "Advanced" ) );
773 m_paramGrid->HideProperty( "Advanced" );
774
775 m_paramGrid->Append( new wxPropertyCategory( "Flags" ) );
776 m_paramGrid->HideProperty( "Flags" );
777
778 m_paramGrid->CollapseAll();
779
780 for( int i = 0; i < aModel->GetParamCount(); ++i )
781 addParamPropertyIfRelevant( aModel, i );
782
783 m_paramGrid->CollapseAll();
784 m_paramGrid->Expand( "AC" );
785 m_paramGrid->Expand( "Waveform" );
786 }
787
788 adjustParamGridColumns( m_paramGrid->GetGrid()->GetSize().GetX(), true );
789
790 // Set all properties to default colors.
791 // Update properties in models that have autofill.
792 for( wxPropertyGridIterator it = m_paramGrid->GetIterator(); !it.AtEnd(); ++it )
793 {
794 wxColour bgCol = m_paramGrid->GetGrid()->GetPropertyDefaultCell().GetBgCol();
795 wxColour fgCol = m_paramGrid->GetGrid()->GetPropertyDefaultCell().GetFgCol();
796
797 for( int col = 0; col < m_paramGridMgr->GetColumnCount(); ++col )
798 {
799 ( *it )->GetCell( col ).SetBgCol( bgCol );
800 ( *it )->GetCell( col ).SetFgCol( fgCol );
801 }
802
803 SIM_PROPERTY* prop = dynamic_cast<SIM_PROPERTY*>( *it );
804
805 if( !prop )
806 continue;
807
808 const SIM_MODEL::PARAM& param = prop->GetParam();
809
810 // Model values other than the currently edited value may have changed. Update them.
811 // This feature is called "autofill" and present only in certain models. Don't do it for
812 // models that don't have it for performance reasons.
813 if( aModel->HasAutofill() )
814 ( *it )->SetValueFromString( param.value );
815 }
816}
817
818
819template <typename T>
821{
822 if( dynamic_cast<SIM_MODEL_SPICE_FALLBACK*>( aModel ) )
823 return;
824
825 wxString text;
826 SPICE_ITEM item;
827
828 item.modelName = m_modelListBox->GetStringSelection();
829
830 if( m_rbBuiltinModel->GetValue() || item.modelName == "" )
832
833 text << aModel->SpiceGenerator().Preview( item );
834
835 m_codePreview->SetText( text );
836 m_codePreview->SelectNone();
837}
838
839
840template <typename T>
841void DIALOG_SIM_MODEL<T>::updatePinAssignments( SIM_MODEL* aModel, bool aForceRefreshFromModel )
842{
843 m_pinAssignmentsGrid->CommitPendingChanges( true );
844
845 if( m_pinAssignmentsGrid->GetNumberRows() == 0 )
846 {
847 m_pinAssignmentsGrid->AppendRows( (int) m_sortedPartPins.size() );
848
849 for( int ii = 0; ii < m_pinAssignmentsGrid->GetNumberRows(); ++ii )
850 {
851 wxString symbolPinString = getSymbolPinString( ii );
852
853 m_pinAssignmentsGrid->SetReadOnly( ii, PIN_COLUMN::SYMBOL );
854 m_pinAssignmentsGrid->SetCellValue( ii, PIN_COLUMN::SYMBOL, symbolPinString );
855 }
856
857 aForceRefreshFromModel = true;
858 }
859
860 if( aForceRefreshFromModel )
861 {
862 // Reset the grid.
863 for( int row = 0; row < m_pinAssignmentsGrid->GetNumberRows(); ++row )
864 m_pinAssignmentsGrid->SetCellValue( row, PIN_COLUMN::MODEL, _( "Not Connected" ) );
865
866 // Now set up the grid values in the Model column.
867 for( int modelPinIndex = 0; modelPinIndex < aModel->GetPinCount(); ++modelPinIndex )
868 {
869 wxString symbolPinNumber = aModel->GetPin( modelPinIndex ).symbolPinNumber;
870
871 if( symbolPinNumber == "" )
872 continue;
873
874 int symbolPinRow = findSymbolPinRow( symbolPinNumber );
875
876 if( symbolPinRow == -1 )
877 continue;
878
879 wxString modelPinString = getModelPinString( aModel, modelPinIndex );
880 m_pinAssignmentsGrid->SetCellValue( symbolPinRow, PIN_COLUMN::MODEL, modelPinString );
881 }
882 }
883
884 for( int ii = 0; ii < m_pinAssignmentsGrid->GetNumberRows(); ++ii )
885 {
886 // Set up the Model column cell editors with dropdown options.
887 std::vector<BITMAPS> modelPinIcons;
888 wxArrayString modelPinChoices;
889
890 for( int jj = 0; jj < aModel->GetPinCount(); ++jj )
891 {
892 if( aModel->GetPin( jj ).symbolPinNumber != "" )
893 modelPinIcons.push_back( PinShapeGetBitmap( GRAPHIC_PINSHAPE::LINE ) );
894 else
895 modelPinIcons.push_back( BITMAPS::INVALID_BITMAP );
896
897 modelPinChoices.Add( getModelPinString( aModel, jj ) );
898 }
899
900 modelPinIcons.push_back( BITMAPS::INVALID_BITMAP );
901 modelPinChoices.Add( _( "Not Connected" ) );
902
903 // This is not a memory leak; `SetCellEditor()` calls `DecRef()` on its previous editor.
904 m_pinAssignmentsGrid->SetCellEditor( ii, PIN_COLUMN::MODEL,
905 new GRID_CELL_ICON_TEXT_POPUP( modelPinIcons, modelPinChoices ) );
906
907 // Assignment stays the same, but model pin names need to be updated
908 int modelPinIndex = getModelPinIndex( m_pinAssignmentsGrid->GetCellValue( ii, PIN_COLUMN::MODEL ) );
909
910 if( modelPinIndex >= 0 )
911 m_pinAssignmentsGrid->SetCellValue( ii, PIN_COLUMN::MODEL, getModelPinString( aModel, modelPinIndex ) );
912 }
913
914 // TODO: Show a preview of the symbol with the pin numbers shown.
915
916 if( aModel->GetType() == SIM_MODEL::TYPE::SUBCKT )
917 {
918 SIM_MODEL_SUBCKT* subckt = static_cast<SIM_MODEL_SUBCKT*>( aModel );
919 m_subckt->SetText( subckt->GetSpiceCode() );
920 m_subckt->SetEditable( false );
921 }
922 else
923 {
924 m_subcktLabel->Show( false );
925 m_subckt->Show( false );
926 }
927}
928
929
930template <typename T>
932{
933 for( int i = 0; i < aModel->GetPinCount(); ++i )
934 {
935 if( !m_symbol.GetPin( aModel->GetPin( i ).symbolPinNumber ) )
936 aModel->AssignSymbolPinNumberToModelPin( i, "" );
937 }
938}
939
940
941template <typename T>
942bool DIALOG_SIM_MODEL<T>::loadLibrary( const wxString& aLibraryPath, REPORTER& aReporter, bool aForceReload )
943{
944 if( m_prevLibrary == aLibraryPath && !aForceReload )
945 return true;
946
947 m_libraryModelsMgr.SetForceFullParse();
948 m_libraryModelsMgr.SetLibrary( aLibraryPath, aReporter );
949 m_libraryPathText->ChangeValue( aLibraryPath );
951
953 {
954 m_libraryModelsMgr.Clear();
955
956 if( m_modelListBox->GetSelection() != wxNOT_FOUND )
957 m_modelListBox->SetSelection( wxNOT_FOUND );
958
959 if( m_modelListBox->GetCount() )
960 m_modelListBox->Clear();
961
962 wxArrayString emptyArray;
963 m_pinModelCombobox->Set( emptyArray );
964 m_pinCombobox->Set( emptyArray );
965 m_pinModelCombobox->SetSelection( -1 );
966 m_pinCombobox->SetSelection( -1 );
967 m_waveformChoice->Clear();
968 m_prevLibrary.Clear();
969
970 return false;
971 }
972
973 std::string modelName = GetFieldValue( &m_fields, SIM_LIBRARY::NAME_FIELD, true, 0 );
974
975 const auto partPinNumbers = SIM_MODEL::PinNumbers( m_sortedPartPins );
976
977 for( const auto& [baseModelName, baseModel] : library()->GetModels() )
978 {
979 if( baseModelName == modelName )
980 m_libraryModelsMgr.CreateModel( &baseModel, partPinNumbers, m_fields, true, 0, aReporter );
981 else
982 m_libraryModelsMgr.CreateModel( &baseModel, partPinNumbers, aReporter );
983 }
984
985 m_rbLibraryModel->SetValue( true );
986
987 wxArrayString modelNames;
988
989 bool modelNameExists = false;
990 for( const auto& [name, model] : library()->GetModels() )
991 {
992 modelNames.Add( name );
994
995 if( wxString( name ).IsSameAs( modelName, false ) )
996 modelNameExists = true;
997 }
998
999 modelNames.Sort();
1000
1001 m_modelListBox->Clear();
1002 m_modelListBox->Append( modelNames );
1003
1004 if( !modelNameExists )
1005 {
1006 m_infoBar->ShowMessage( wxString::Format( _( "No model named '%s' in '%s'." ),
1007 modelName,
1008 aLibraryPath ) );
1009 return false;
1010 }
1011
1012 if( isIbisLoaded() )
1013 {
1014 wxArrayString emptyArray;
1015 m_pinModelCombobox->Set( emptyArray );
1016 m_pinCombobox->Set( emptyArray );
1017 m_pinModelCombobox->SetSelection( -1 );
1018 m_pinCombobox->SetSelection( -1 );
1019 }
1020
1021 m_modelListBox->SetStringSelection( modelName );
1022
1023 if( m_modelListBox->GetSelection() < 0 && m_modelListBox->GetCount() > 0 )
1024 {
1025 m_modelListBox->SetSelection( 0 ); // WARNING: SetSelection() does NOT fire events
1026 wxCommandEvent dummy;
1028 }
1029
1030 m_curModelType = curModel().GetType();
1031
1032 m_prevLibrary = aLibraryPath;
1033 return true;
1034}
1035
1036
1037template <typename T>
1039{
1040 if( aModel->GetParam( aParamIndex ).info.dir == SIM_MODEL::PARAM::DIR_OUT )
1041 return;
1042
1043 switch( aModel->GetParam( aParamIndex ).info.category )
1044 {
1045 case CATEGORY::AC:
1046 m_paramGrid->HideProperty( "AC", false );
1047 m_paramGrid->AppendIn( "AC", newParamProperty( aModel, aParamIndex ) );
1048 break;
1049
1050 case CATEGORY::DC:
1051 m_paramGrid->HideProperty( "DC", false );
1052 m_paramGrid->AppendIn( "DC", newParamProperty( aModel, aParamIndex ) );
1053 break;
1054
1055 case CATEGORY::S_PARAM:
1056 m_paramGrid->HideProperty( "S-Parameters", false );
1057 m_paramGrid->AppendIn( "S-Parameters", newParamProperty( aModel, aParamIndex ) );
1058 break;
1059
1060 case CATEGORY::CAPACITANCE:
1061 m_paramGrid->HideProperty( "Capacitance", false );
1062 m_paramGrid->AppendIn( "Capacitance", newParamProperty( aModel, aParamIndex ) );
1063 break;
1064
1065 case CATEGORY::TEMPERATURE:
1066 m_paramGrid->HideProperty( "Temperature", false );
1067 m_paramGrid->AppendIn( "Temperature", newParamProperty( aModel, aParamIndex ) );
1068 break;
1069
1070 case CATEGORY::NOISE:
1071 m_paramGrid->HideProperty( "Noise", false );
1072 m_paramGrid->AppendIn( "Noise", newParamProperty( aModel, aParamIndex ) );
1073 break;
1074
1075 case CATEGORY::DISTRIBUTED_QUANTITIES:
1076 m_paramGrid->HideProperty( "Distributed Quantities", false );
1077 m_paramGrid->AppendIn( "Distributed Quantities", newParamProperty( aModel, aParamIndex ) );
1078 break;
1079
1080 case CATEGORY::WAVEFORM:
1081 m_paramGrid->HideProperty( "Waveform", false );
1082 m_paramGrid->AppendIn( "Waveform", newParamProperty( aModel, aParamIndex ) );
1083 break;
1084
1085 case CATEGORY::GEOMETRY:
1086 m_paramGrid->HideProperty( "Geometry", false );
1087 m_paramGrid->AppendIn( "Geometry", newParamProperty( aModel, aParamIndex ) );
1088 break;
1089
1090 case CATEGORY::LIMITING_VALUES:
1091 m_paramGrid->HideProperty( "Limiting Values", false );
1092 m_paramGrid->AppendIn( "Limiting Values", newParamProperty( aModel, aParamIndex ) );
1093 break;
1094
1095 case CATEGORY::ADVANCED:
1096 m_paramGrid->HideProperty( "Advanced", false );
1097 m_paramGrid->AppendIn( "Advanced", newParamProperty( aModel, aParamIndex ) );
1098 break;
1099
1100 case CATEGORY::FLAGS:
1101 m_paramGrid->HideProperty( "Flags", false );
1102 m_paramGrid->AppendIn( "Flags", newParamProperty( aModel, aParamIndex ) );
1103 break;
1104
1105 default:
1106 m_paramGrid->Insert( m_firstCategory, newParamProperty( aModel, aParamIndex ) );
1107 break;
1108
1109 case CATEGORY::INITIAL_CONDITIONS:
1110 case CATEGORY::SUPERFLUOUS:
1111 return;
1112 }
1113}
1114
1115
1116template <typename T>
1117wxPGProperty* DIALOG_SIM_MODEL<T>::newParamProperty( SIM_MODEL* aModel, int aParamIndex ) const
1118{
1119 const SIM_MODEL::PARAM& param = aModel->GetParam( aParamIndex );
1120 wxString paramDescription;
1121
1122 if( param.info.description == "" )
1123 paramDescription = wxString::Format( "%s", param.info.name );
1124 else
1125 paramDescription = wxString::Format( "%s (%s)", param.info.description, param.info.name );
1126
1127 wxPGProperty* prop = nullptr;
1128
1129 switch( param.info.type )
1130 {
1132 prop = new SIM_BOOL_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex );
1133 prop->SetAttribute( wxPG_BOOL_USE_CHECKBOX, true );
1134 break;
1135
1137 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
1139 break;
1140
1142 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
1144 break;
1145
1146 //case TYPE_COMPLEX:
1147 // break;
1148
1150 // Special case: K-line mutual inductance statement parameters l1 and l2 are references
1151 // to other inductors in the circuit.
1152 if( dynamic_cast<SIM_MODEL_L_MUTUAL*>( aModel ) != nullptr
1153 && ( param.info.name == "l1" || param.info.name == "l2" ) )
1154 {
1155 wxArrayString inductors;
1156
1157 if( SCH_EDIT_FRAME* schEditFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
1158 {
1159 SPICE_CIRCUIT_MODEL circuit( &schEditFrame->Schematic(), &schEditFrame->Kiway() );
1160 NULL_REPORTER devNul;
1161
1163
1164 for( const SPICE_ITEM& item : circuit.GetItems() )
1165 {
1166 if( item.model->GetDeviceType() == SIM_MODEL::DEVICE_T::L )
1167 inductors.push_back( item.refName );
1168 }
1169
1170 inductors.Sort(
1171 []( const wxString& a, const wxString& b ) -> int
1172 {
1173 return StrNumCmp( a, b, true );
1174 } );
1175 }
1176
1177 if( inductors.empty() )
1178 {
1179 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
1181 }
1182 else
1183 {
1184 prop = new SIM_ENUM_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
1185 inductors );
1186 }
1187 }
1188 else if( param.info.enumValues.empty() )
1189 {
1190 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
1192 }
1193 else
1194 {
1195 wxArrayString values;
1196
1197 for( const std::string& string : aModel->GetParam( aParamIndex ).info.enumValues )
1198 values.Add( string );
1199
1200 prop = new SIM_ENUM_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
1201 values );
1202 }
1203 break;
1204
1205 default:
1206 prop = new wxStringProperty( paramDescription, param.info.name );
1207 break;
1208 }
1209
1210 prop->SetAttribute( wxPG_ATTR_UNITS, wxString::FromUTF8( param.info.unit.c_str() ) );
1211
1212 // Legacy due to the way we extracted the parameters from Ngspice.
1213 prop->SetCell( 3, wxString::FromUTF8( param.info.defaultValue ) );
1214
1215 wxString typeStr;
1216
1217 switch( param.info.type )
1218 {
1219 case SIM_VALUE::TYPE_BOOL: typeStr = wxT( "Bool" ); break;
1220 case SIM_VALUE::TYPE_INT: typeStr = wxT( "Int" ); break;
1221 case SIM_VALUE::TYPE_FLOAT: typeStr = wxT( "Float" ); break;
1222 case SIM_VALUE::TYPE_COMPLEX: typeStr = wxT( "Complex" ); break;
1223 case SIM_VALUE::TYPE_STRING: typeStr = wxT( "String" ); break;
1224 case SIM_VALUE::TYPE_BOOL_VECTOR: typeStr = wxT( "Bool Vector" ); break;
1225 case SIM_VALUE::TYPE_INT_VECTOR: typeStr = wxT( "Int Vector" ); break;
1226 case SIM_VALUE::TYPE_FLOAT_VECTOR: typeStr = wxT( "Float Vector" ); break;
1227 case SIM_VALUE::TYPE_COMPLEX_VECTOR: typeStr = wxT( "Complex Vector" ); break;
1228 }
1229
1230 prop->SetCell( PARAM_COLUMN::TYPE, typeStr );
1231
1232 return prop;
1233}
1234
1235
1236template <typename T>
1237int DIALOG_SIM_MODEL<T>::findSymbolPinRow( const wxString& aSymbolPinNumber ) const
1238{
1239 for( int row = 0; row < static_cast<int>( m_sortedPartPins.size() ); ++row )
1240 {
1242
1243 if( pin->GetNumber() == aSymbolPinNumber )
1244 return row;
1245 }
1246
1247 return -1;
1248}
1249
1250
1251template <typename T>
1253{
1254 if( m_rbLibraryModel->GetValue() )
1255 {
1256 wxString sel = m_modelListBox->GetStringSelection();
1257
1258 if( m_modelListBoxEntryToLibraryIdx.contains( sel ) )
1259 {
1260 int idx = m_modelListBoxEntryToLibraryIdx.at( sel );
1261
1262 if( idx >= 0 && idx < (int) m_libraryModelsMgr.GetModels().size() )
1263 return m_libraryModelsMgr.GetModels().at( idx ).get();
1264 }
1265 }
1266 else
1267 {
1268 if( (int) m_curModelType < (int) m_builtinModelsMgr.GetModels().size() )
1269 return m_builtinModelsMgr.GetModels().at( static_cast<int>( m_curModelType ) );
1270 }
1271
1272 return m_builtinModelsMgr.GetModels().at( (int) SIM_MODEL::TYPE::NONE );
1273}
1274
1275
1276template <typename T>
1278{
1279 if( m_libraryModelsMgr.GetLibraries().size() == 1 )
1280 return &m_libraryModelsMgr.GetLibraries().begin()->second.get();
1281
1282 return nullptr;
1283}
1284
1285
1286template <typename T>
1287wxString DIALOG_SIM_MODEL<T>::getSymbolPinString( int symbolPinIndex ) const
1288{
1289 SCH_PIN* pin = m_sortedPartPins.at( symbolPinIndex );
1290 wxString pinNumber;
1291 wxString pinName;
1292
1293 if( pin )
1294 {
1295 pinNumber = pin->GetShownNumber();
1296 pinName = pin->GetShownName();
1297 }
1298
1299 if( !pinName.IsEmpty() && pinName != pinNumber )
1300 pinNumber += wxString::Format( wxT( " (\"%s\")" ), pinName );
1301
1302 return pinNumber;
1303}
1304
1305
1306template <typename T>
1307wxString DIALOG_SIM_MODEL<T>::getModelPinString( SIM_MODEL* aModel, int aModelPinIndex ) const
1308{
1309 wxString modelPinName;
1310
1311 if( aModelPinIndex >= 0 && aModelPinIndex < aModel->GetPinCount() )
1312 modelPinName = aModel->GetPin( aModelPinIndex ).modelPinName;
1313
1314 wxString modelPinNumber = wxString::Format( "%d", aModelPinIndex + 1 );
1315
1316 if( !modelPinName.IsEmpty() && modelPinName != modelPinNumber )
1317 modelPinNumber += wxString::Format( wxT( " (\"%s\")" ), modelPinName );
1318
1319 return modelPinNumber;
1320}
1321
1322
1323template <typename T>
1324int DIALOG_SIM_MODEL<T>::getModelPinIndex( const wxString& aModelPinString ) const
1325{
1326 if( aModelPinString == "Not Connected" )
1328
1329 int length = aModelPinString.Find( " " );
1330
1331 if( length == wxNOT_FOUND )
1332 length = static_cast<int>( aModelPinString.Length() );
1333
1334 long result = 0;
1335 aModelPinString.Mid( 0, length ).ToCLong( &result );
1336
1337 return static_cast<int>( result - 1 );
1338}
1339
1340
1341template <typename T>
1342void DIALOG_SIM_MODEL<T>::onRadioButton( wxCommandEvent& aEvent )
1343{
1344 m_prevModel = nullptr; // Ensure the Model panel will be rebuild after updating other params.
1345 updateWidgets();
1346}
1347
1348
1349template <typename T>
1350void DIALOG_SIM_MODEL<T>::onLibraryPathText( wxCommandEvent& aEvent )
1351{
1352 m_rbLibraryModel->SetValue( true );
1353}
1354
1355
1356template <typename T>
1358{
1359 m_rbLibraryModel->SetValue( true );
1360
1362 wxString path = m_libraryPathText->GetValue();
1363
1364 if( loadLibrary( path, reporter, true ) || path.IsEmpty() )
1365 m_infoBar->Hide();
1366 else if( reporter.HasMessage() )
1367 m_infoBar->ShowMessage( reporter.GetMessages() );
1368
1369 updateWidgets();
1370}
1371
1372
1373template <typename T>
1375{
1376 CallAfter(
1377 [this]()
1378 {
1379 // Disable logging -- otherwise we'll end up in an endless loop of show-log,
1380 // kill-focus, show-log, kill-focus, etc.
1381 wxLogNull doNotLog;
1382
1383 wxCommandEvent dummy;
1385 } );
1386
1387 aEvent.Skip(); // mandatory in wxFocusEvent events
1388}
1389
1390
1391template <typename T>
1392void DIALOG_SIM_MODEL<T>::onBrowseButtonClick( wxCommandEvent& aEvent )
1393{
1394 static wxString s_mruPath;
1395
1396 wxString path = s_mruPath.IsEmpty() ? Prj().GetProjectPath() : s_mruPath;
1397 wxFileDialog dlg( this, _( "Browse Models" ), path );
1399
1400 dlg.SetCustomizeHook( customize );
1401
1403
1404 if( dlg.ShowModal() == wxID_CANCEL )
1405 return;
1406
1407 m_rbLibraryModel->SetValue( true );
1408
1409 path = dlg.GetPath();
1410 wxFileName fn( path );
1411 s_mruPath = fn.GetPath();
1412
1413 if( customize.GetEmbed() )
1414 {
1415 EMBEDDED_FILES::EMBEDDED_FILE* result = m_filesStack[0]->AddFile( fn, false );
1416 path = result->GetLink();
1417 }
1418 else if( fn.MakeRelativeTo( Prj().GetProjectPath() ) && !fn.GetFullPath().StartsWith( wxS( ".." ) ) )
1419 {
1420 path = fn.GetFullPath();
1421 }
1422
1424
1425 if( loadLibrary( path, reporter, true ) )
1426 m_infoBar->Hide();
1427 else
1428 m_infoBar->ShowMessage( reporter.GetMessages() );
1429
1430 updateWidgets();
1431}
1432
1433
1434template <typename T>
1435void DIALOG_SIM_MODEL<T>::onFilterCharHook( wxKeyEvent& aKeyStroke )
1436{
1437 int count = m_modelListBox->GetCount();
1438
1439 if( count == 0 )
1440 {
1441 aKeyStroke.Skip();
1442 return;
1443 }
1444
1445 int sel = m_modelListBox->GetSelection();
1446
1447 switch( aKeyStroke.GetKeyCode() )
1448 {
1449 case WXK_UP:
1450 if( sel == wxNOT_FOUND )
1451 sel = count - 1;
1452 else
1453 sel = std::max( sel - 1, 0 );
1454
1455 break;
1456
1457 case WXK_DOWN:
1458 if( sel == wxNOT_FOUND )
1459 sel = 0;
1460 else
1461 sel = std::min( sel + 1, count - 1 );
1462
1463 break;
1464
1465 case WXK_RETURN:
1466 wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
1467 return;
1468
1469 default:
1470 aKeyStroke.Skip();
1471 return;
1472 }
1473
1474 m_modelListBox->SetSelection( sel ); // WARNING: SetSelection() does NOT fire events
1475 wxCommandEvent dummy;
1477}
1478
1479
1480template <typename T>
1481void DIALOG_SIM_MODEL<T>::onModelFilter( wxCommandEvent& aEvent )
1482{
1483 if( library() == nullptr ) // No library loaded
1484 return;
1485
1486 wxArrayString modelNames;
1487 wxString current = m_modelListBox->GetStringSelection();
1488 wxString filter = wxT( "*" ) + m_modelFilter->GetValue() + wxT( "*" );
1489
1490 for( const auto& [name, model] : library()->GetModels() )
1491 {
1492 wxString wx_name( name );
1493
1494 if( wx_name.Matches( filter ) )
1495 modelNames.Add( wx_name );
1496 }
1497
1498 modelNames.Sort();
1499
1500 m_modelListBox->Clear();
1501 m_modelListBox->Append( modelNames );
1502
1503 if( m_modelListBox->GetCount() > 0 )
1504 {
1505 if( !m_modelListBox->SetStringSelection( current ) )
1506 {
1507 m_modelListBox->SetSelection( 0 ); // WARNING: SetSelection() does NOT fire events
1508 wxCommandEvent dummy;
1510 }
1511 }
1512}
1513
1514
1515template <typename T>
1516void DIALOG_SIM_MODEL<T>::onModelNameChoice( wxCommandEvent& aEvent )
1517{
1518 if( isIbisLoaded() )
1519 {
1520 wxArrayString pinLabels;
1521 SIM_MODEL_IBIS* modelkibis = dynamic_cast<SIM_MODEL_IBIS*>( &curModel() );
1522
1523 wxCHECK2( modelkibis, return );
1524
1525 for( std::pair<wxString, wxString> strs : modelkibis->GetIbisPins() )
1526 pinLabels.Add( strs.first + wxT( " - " ) + strs.second );
1527
1528 m_pinCombobox->Set( pinLabels );
1529
1530 wxArrayString emptyArray;
1531 m_pinModelCombobox->Set( emptyArray );
1532 }
1533
1534 m_rbLibraryModel->SetValue( true );
1535
1536 if( SIM_MODEL_SPICE_FALLBACK* fallback =
1537 dynamic_cast<SIM_MODEL_SPICE_FALLBACK*>( &curModel() ) )
1538 {
1539 wxArrayString lines = wxSplit( fallback->GetSpiceCode(), '\n' );
1540 wxString code;
1541
1542 for( const wxString& line : lines )
1543 {
1544 if( !line.StartsWith( '*' ) )
1545 {
1546 if( !code.IsEmpty() )
1547 code += "\n";
1548
1549 code += line;
1550 }
1551 }
1552
1553 m_infoBar->ShowMessage( wxString::Format( _( "Failed to parse:\n\n"
1554 "%s\n"
1555 "Using generic SPICE model." ),
1556 code ) );
1557 }
1558 else
1559 {
1560 m_infoBar->Hide();
1561 }
1562
1563 updateWidgets();
1564}
1565
1566
1567template <typename T>
1568void DIALOG_SIM_MODEL<T>::onPinCombobox( wxCommandEvent& aEvent )
1569{
1570 wxArrayString modelLabels;
1571
1572 SIM_MODEL_IBIS& ibisModel = static_cast<SIM_MODEL_IBIS&>( curModel() );
1573
1574 std::vector<std::pair<std::string, std::string>> strs = ibisModel.GetIbisPins();
1575 std::string pinNumber = strs.at( m_pinCombobox->GetSelection() ).first;
1576
1577 const SIM_LIBRARY_IBIS* ibisLibrary = dynamic_cast<const SIM_LIBRARY_IBIS*>( library() );
1578
1579 ibisModel.ChangePin( *ibisLibrary, pinNumber );
1580
1581 ibisModel.m_enableDiff = ibisLibrary->isPinDiff( ibisModel.GetComponentName(), pinNumber );
1582
1583 for( wxString modelName : ibisModel.GetIbisModels() )
1584 modelLabels.Add( modelName );
1585
1586 m_pinModelCombobox->Set( modelLabels );
1587
1588 if( m_pinModelCombobox->GetCount() == 1 )
1589 m_pinModelCombobox->SetSelection( 0 );
1590 else
1591 m_pinModelCombobox->SetSelection( -1 );
1592
1593 updateWidgets();
1594}
1595
1596
1597template <typename T>
1599{
1600 m_pinCombobox->SetSelection( m_pinCombobox->FindString( m_pinCombobox->GetValue() ) );
1601
1602 onPinModelCombobox( aEvent );
1603}
1604
1605
1606template <typename T>
1607void DIALOG_SIM_MODEL<T>::onPinModelCombobox( wxCommandEvent& aEvent )
1608{
1609 if( SIM_MODEL_IBIS* ibismodel = dynamic_cast<SIM_MODEL_IBIS*>( &curModel() ) )
1610 {
1611 const SIM_LIBRARY_IBIS* ibisLibrary = dynamic_cast<const SIM_LIBRARY_IBIS*>( library() );
1612
1613 // Skip uncommitted combobox text so SetIbisModel doesn't silently fail.
1614 if( ibisLibrary
1615 && m_pinCombobox->GetSelection() >= 0
1616 && m_pinModelCombobox->GetSelection() >= 0 )
1617 {
1618 std::string pinNumber = ibismodel->GetIbisPins()
1619 .at( m_pinCombobox->GetSelection() )
1620 .first;
1621 std::string modelName = m_pinModelCombobox->GetStringSelection().ToStdString();
1622
1623 ibismodel->SetIbisModel( *ibisLibrary, pinNumber, modelName );
1624 }
1625 }
1626
1627 updateWidgets();
1628}
1629
1630
1631template <typename T>
1633{
1634 m_pinModelCombobox->SetSelection( m_pinModelCombobox->FindString( m_pinModelCombobox->GetValue() ) );
1635}
1636
1637
1638template <typename T>
1640{
1641 if( SIM_MODEL_IBIS* modelibis = dynamic_cast<SIM_MODEL_IBIS*>( &curModel() ) )
1642 {
1643 bool diff = m_differentialCheckbox->GetValue() && modelibis->CanDifferential();
1644 modelibis->SwitchSingleEndedDiff( diff );
1645
1646 updateWidgets();
1647 }
1648}
1649
1650
1651template <typename T>
1652void DIALOG_SIM_MODEL<T>::onDeviceTypeChoice( wxCommandEvent& aEvent )
1653{
1654 m_rbBuiltinModel->SetValue( true );
1655
1656 for( SIM_MODEL::DEVICE_T deviceType : SIM_MODEL::DEVICE_T_ITERATOR() )
1657 {
1658 if( SIM_MODEL::DeviceInfo( deviceType ).description == m_deviceChoice->GetStringSelection() )
1659 {
1660 m_curModelType = m_curModelTypeOfDeviceType.at( deviceType );
1661 break;
1662 }
1663 }
1664
1665 updateWidgets();
1666}
1667
1668
1669template <typename T>
1670void DIALOG_SIM_MODEL<T>::onWaveformChoice( wxCommandEvent& aEvent )
1671{
1672 SIM_MODEL::DEVICE_T deviceType = curModel().GetDeviceType();
1673 wxString typeDescription = m_waveformChoice->GetStringSelection();
1674
1675 for( SIM_MODEL::TYPE type : { SIM_MODEL::TYPE::KIBIS_DEVICE,
1676 SIM_MODEL::TYPE::KIBIS_DRIVER_DC,
1677 SIM_MODEL::TYPE::KIBIS_DRIVER_RECT,
1678 SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS } )
1679 {
1680 if( equivalent( deviceType, SIM_MODEL::TypeInfo( type ).deviceType )
1681 && typeDescription == SIM_MODEL::TypeInfo( type ).description )
1682 {
1683 int idx = 0;
1684 wxString sel = m_modelListBox->GetStringSelection();
1685
1686 if( m_modelListBoxEntryToLibraryIdx.contains( sel ) )
1687 idx = m_modelListBoxEntryToLibraryIdx.at( sel );
1688
1689 SIM_MODEL_IBIS& baseModel = static_cast<SIM_MODEL_IBIS&>( m_libraryModelsMgr.GetModels()[idx].get() );
1690
1691 m_libraryModelsMgr.SetModel( idx, std::make_unique<SIM_MODEL_IBIS>( type, baseModel ) );
1692
1693 try
1694 {
1695 m_libraryModelsMgr.GetModels()[idx].get().ReadDataFields(
1697 }
1698 catch( IO_ERROR& err )
1699 {
1700 DisplayErrorMessage( this, err.What() );
1701 }
1702
1703 m_curModelType = type;
1704 break;
1705 }
1706 }
1707
1708 m_curModelTypeOfDeviceType.at( deviceType ) = m_curModelType;
1709 updateWidgets();
1710}
1711
1712
1713template <typename T>
1714void DIALOG_SIM_MODEL<T>::onTypeChoice( wxCommandEvent& aEvent )
1715{
1716 SIM_MODEL::DEVICE_T deviceType = curModel().GetDeviceType();
1717 wxString typeDescription = m_deviceSubtypeChoice->GetStringSelection();
1718
1719 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1720 {
1721 if( equivalent( deviceType, SIM_MODEL::TypeInfo( type ).deviceType )
1722 && typeDescription == SIM_MODEL::TypeInfo( type ).description )
1723 {
1724 m_curModelType = type;
1725 break;
1726 }
1727 }
1728
1729 m_curModelTypeOfDeviceType.at( deviceType ) = m_curModelType;
1730 updateWidgets();
1731}
1732
1733
1734template <typename T>
1735void DIALOG_SIM_MODEL<T>::onPageChanging( wxBookCtrlEvent& event )
1736{
1738}
1739
1740
1741template <typename T>
1743{
1744 int symbolPinIndex = aEvent.GetRow();
1745 wxString oldModelPinName = aEvent.GetString();
1746 wxString modelPinName = m_pinAssignmentsGrid->GetCellValue( aEvent.GetRow(), aEvent.GetCol() );
1747
1748 int oldModelPinIndex = getModelPinIndex( oldModelPinName );
1749 int modelPinIndex = getModelPinIndex( modelPinName );
1750
1751 if( oldModelPinIndex != SIM_MODEL_PIN::NOT_CONNECTED )
1752 curModel().AssignSymbolPinNumberToModelPin( oldModelPinIndex, "" );
1753
1754 if( modelPinIndex != SIM_MODEL_PIN::NOT_CONNECTED )
1755 {
1756 SCH_PIN* symbolPin = m_sortedPartPins.at( symbolPinIndex );
1757
1758 curModel().AssignSymbolPinNumberToModelPin( modelPinIndex, symbolPin->GetShownNumber() );
1759 }
1760
1762
1763 aEvent.Skip();
1764}
1765
1766
1767template <typename T>
1769{
1770 wxGridUpdateLocker deferRepaintsTillLeavingScope( m_pinAssignmentsGrid );
1771
1773 m_pinAssignmentsGrid->SetColSize( PIN_COLUMN::MODEL, gridWidth / 2 );
1774 m_pinAssignmentsGrid->SetColSize( PIN_COLUMN::SYMBOL, gridWidth / 2 );
1775
1776 aEvent.Skip();
1777}
1778
1779
1780template <typename T>
1782{
1783 // By default, when a property grid is focused, the textbox is not immediately focused until
1784 // Tab key is pressed. This is inconvenient, so we fix that here.
1785
1786 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1787 wxPGProperty* selected = grid->GetSelection();
1788
1789 if( !selected )
1790 selected = grid->wxPropertyGridInterface::GetFirst();
1791
1792#if wxCHECK_VERSION( 3, 3, 0 )
1793 if( selected )
1794 grid->DoSelectProperty( selected, wxPGSelectPropertyFlags::Focus );
1795#else
1796 if( selected )
1797 grid->DoSelectProperty( selected, wxPG_SEL_FOCUS );
1798#endif
1799
1800 aEvent.Skip();
1801}
1802
1803
1804template <typename T>
1805void DIALOG_SIM_MODEL<T>::onParamGridSelectionChange( wxPropertyGridEvent& aEvent )
1806{
1807 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1808
1809 // Jump over categories.
1810 if( grid->GetSelection() && grid->GetSelection()->IsCategory() )
1811 {
1812 wxPGProperty* selection = grid->GetSelection();
1813
1814 // If the new selection is immediately above the previous selection, we jump up. Otherwise
1815 // we jump down. We do this by simulating up or down arrow keys.
1816
1817 wxPropertyGridIterator it = grid->GetIterator( wxPG_ITERATE_VISIBLE, selection );
1818 it.Next();
1819
1820 wxKeyEvent* keyEvent = new wxKeyEvent( wxEVT_KEY_DOWN );
1821
1822 if( *it == m_prevParamGridSelection )
1823 {
1824 if( !selection->IsExpanded() )
1825 {
1826 grid->Expand( selection );
1827 keyEvent->m_keyCode = WXK_DOWN;
1828 wxQueueEvent( grid, keyEvent );
1829
1830 // Does not work for some reason.
1831 /*m_paramGrid->DoSelectProperty( selection->Item( selection->GetChildCount() - 1 ),
1832 wxPG_SEL_FOCUS );*/
1833 }
1834 else
1835 {
1836 keyEvent->m_keyCode = WXK_UP;
1837 wxQueueEvent( grid, keyEvent );
1838 }
1839 }
1840 else
1841 {
1842 if( !selection->IsExpanded() )
1843 grid->Expand( selection );
1844
1845 keyEvent->m_keyCode = WXK_DOWN;
1846 wxQueueEvent( grid, keyEvent );
1847 }
1848
1849 m_prevParamGridSelection = grid->GetSelection();
1850 return;
1851 }
1852
1853 wxWindow* editorControl = grid->GetEditorControl();
1854
1855 if( !editorControl )
1856 {
1857 m_prevParamGridSelection = grid->GetSelection();
1858 return;
1859 }
1860
1861 // Without this the user had to press tab before they could edit the field.
1862 editorControl->SetFocus();
1863 m_prevParamGridSelection = grid->GetSelection();
1864}
1865
1866
1867template <typename T>
1868void DIALOG_SIM_MODEL<T>::onUpdateUI( wxUpdateUIEvent& aEvent )
1869{
1870 // This is currently patched in wxPropertyGrid::ScrollWindow() in the Mac wxWidgets fork.
1871 // However, we may need this version if it turns out to be an issue on other platforms and
1872 // we can't get it upstreamed.
1873#if 0
1874 // It's a shame to do this on the UpdateUI event, but neither the wxPropertyGridManager,
1875 // wxPropertyGridPage, wxPropertyGrid, nor the wxPropertyGrid's GetCanvas() window appear
1876 // to get scroll events.
1877
1878 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1879 wxTextCtrl* ctrl = grid->GetEditorTextCtrl();
1880
1881 if( ctrl )
1882 {
1883 wxRect ctrlRect = ctrl->GetScreenRect();
1884 wxRect gridRect = grid->GetScreenRect();
1885
1886 if( ctrlRect.GetTop() < gridRect.GetTop() || ctrlRect.GetBottom() > gridRect.GetBottom() )
1887 grid->ClearSelection();
1888 }
1889#endif
1890}
1891
1892
1893template <typename T>
1894void DIALOG_SIM_MODEL<T>::adjustParamGridColumns( int aWidth, bool aForce )
1895{
1896 wxPropertyGrid* grid = m_paramGridMgr->GetGrid();
1897 int margin = 15;
1898 int indent = 20;
1899
1900 if( aWidth != m_lastParamGridWidth || aForce )
1901 {
1902 m_lastParamGridWidth = aWidth;
1903
1904 grid->FitColumns();
1905
1906 std::vector<int> colWidths;
1907
1908 for( size_t ii = 0; ii < grid->GetColumnCount(); ii++ )
1909 {
1910 if( ii == PARAM_COLUMN::DESCRIPTION )
1911 colWidths.push_back( grid->GetState()->GetColumnWidth( ii ) + margin + indent );
1912 else if( ii == PARAM_COLUMN::VALUE )
1913 colWidths.push_back( std::max( 72, grid->GetState()->GetColumnWidth( ii ) ) + margin );
1914 else
1915 colWidths.push_back( 60 + margin );
1916
1917 aWidth -= colWidths[ ii ];
1918 }
1919
1920 for( size_t ii = 0; ii < grid->GetColumnCount(); ii++ )
1921 grid->SetColumnProportion( ii, colWidths[ ii ] );
1922
1923 grid->ResetColumnSizes();
1924 grid->RefreshEditor();
1925 }
1926}
1927
1928
1929template <typename T>
1931{
1932 adjustParamGridColumns( event.GetSize().GetX(), false );
1933
1934 event.Skip();
1935}
1936
1937
1938template class DIALOG_SIM_MODEL<SCH_SYMBOL>;
1939template class DIALOG_SIM_MODEL<LIB_SYMBOL>;
const char * name
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:106
@ INVALID_BITMAP
wxPropertyGridManager * m_paramGridMgr
DIALOG_SIM_MODEL_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Simulation Model Editor"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
wxStaticText * m_decompositionLabel
wxStaticText * m_deviceSubtypeLabel
STD_BITMAP_BUTTON * m_browseButton
wxStyledTextCtrl * m_subckt
wxPropertyGridPage * m_paramGrid
wxStyledTextCtrl * m_codePreview
const SIM_MODEL * m_prevModel
void onTypeChoice(wxCommandEvent &aEvent) override
void onLibraryPathTextKillFocus(wxFocusEvent &aEvent) override
wxString getSymbolPinString(int aSymbolPinNumber) const
void updateBuiltinModelWidgets(SIM_MODEL *aModel)
void onPinAssignmentsGridCellChange(wxGridEvent &aEvent) override
void onFilterCharHook(wxKeyEvent &aKeyStroke) override
int findSymbolPinRow(const wxString &aSymbolPinNumber) const
void onModelNameChoice(wxCommandEvent &aEvent) override
void onRadioButton(wxCommandEvent &aEvent) override
SCINTILLA_TRICKS * m_scintillaTricksSubckt
bool loadLibrary(const wxString &aLibraryPath, REPORTER &aReporter, bool aForceReload=false)
SIM_LIB_MGR m_builtinModelsMgr
int getModelPinIndex(const wxString &aModelPinString) const
void onModelFilter(wxCommandEvent &aEvent) override
void onLibraryPathText(wxCommandEvent &aEvent) override
void onDifferentialCheckbox(wxCommandEvent &event) override
void onParamGridSelectionChange(wxPropertyGridEvent &aEvent)
void removeOrphanedPinAssignments(SIM_MODEL *aModel)
void adjustParamGridColumns(int aWidth, bool aForce)
std::vector< SCH_FIELD > & m_fields
std::vector< SCH_PIN * > m_sortedPartPins
Pins of the current part.
wxPGProperty * newParamProperty(SIM_MODEL *aModel, int aParamIndex) const
void onDecompositionModeChoice(wxCommandEvent &aEvent) override
wxPGProperty * m_prevParamGridSelection
void onPinModelCombobox(wxCommandEvent &event) override
const SIM_LIBRARY * library() const
SIM_MODEL & curModel() const
SIM_LIB_MGR m_libraryModelsMgr
void onBrowseButtonClick(wxCommandEvent &aEvent) override
void onPinComboboxTextEnter(wxCommandEvent &event) override
void onPinCombobox(wxCommandEvent &event) override
wxPGProperty * m_firstCategory
SCINTILLA_TRICKS * m_scintillaTricksCode
wxString m_inferredValueRestore
std::map< wxString, int > m_modelListBoxEntryToLibraryIdx
void updatePinAssignments(SIM_MODEL *aModel, bool aForceUpdatePins)
void onUpdateUI(wxUpdateUIEvent &aEvent)
DIALOG_SIM_MODEL(wxWindow *aParent, EDA_BASE_FRAME *aFrame, T &aSymbol, std::vector< SCH_FIELD > &aFields)
wxString getModelPinString(SIM_MODEL *aModel, int aModelPinIndex) const
void onPinAssignmentsGridSize(wxSizeEvent &aEvent) override
void updateIbisWidgets(SIM_MODEL *aModel)
void onParamGridSetFocus(wxFocusEvent &aEvent)
void updateModelParamsTab(SIM_MODEL *aModel)
bool TransferDataFromWindow() override
SIM_MODEL::TYPE m_curModelType
void updateModelCodeTab(SIM_MODEL *aModel)
void onSizeParamGrid(wxSizeEvent &event) override
void onPageChanging(wxNotebookEvent &event) override
std::vector< EMBEDDED_FILES * > m_filesStack
void updateDecompositionControls()
Show/populate the decomposition controls (multi-unit only) and sync their enabled state.
void onWaveformChoice(wxCommandEvent &aEvent) override
std::map< SIM_MODEL::DEVICE_T, SIM_MODEL::TYPE > m_curModelTypeOfDeviceType
void onPinModelComboboxTextEnter(wxCommandEvent &event) override
EDA_BASE_FRAME * m_frame
void onLibraryPathTextEnter(wxCommandEvent &aEvent) override
static void RestoreInferredValue(std::vector< SCH_FIELD > &aFields, const wxString &aOriginalValue, bool aOverwritten, bool aStoredInValue)
Restore an inferred passive's Value field after WriteFields().
void onDeviceTypeChoice(wxCommandEvent &aEvent) override
bool TransferDataToWindow() override
void addParamPropertyIfRelevant(SIM_MODEL *aModel, int aParamIndex)
The base frame for deriving all KiCad main window classes.
Add mouse and command handling (such as cut, copy, and paste) to a WX_GRID instance.
Definition grid_tricks.h:57
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
PROJECT & Prj() const
Return a reference to the PROJECT associated with this KIWAY.
Define a library symbol object.
Definition lib_symbol.h:114
void AppendParentEmbeddedFiles(std::vector< EMBEDDED_FILES * > &aStack) const
virtual bool ReadSchematicAndLibraries(unsigned aNetlistOptions, REPORTER &aReporter)
Process the schematic and Spice libraries to create net mapping and a list of SPICE_ITEMs.
const std::list< SPICE_ITEM > & GetItems() const
Return the list of items representing schematic symbols in the Spice world.
A singleton reporter that reports to nowhere.
Definition reporter.h:267
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition project.cpp:276
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition reporter.h:152
EMBEDDED_FILES * GetEmbeddedFiles() override
Schematic editor (Eeschema) main window.
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
void SetText(const wxString &aText) override
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:302
const wxString & GetShownNumber() const
Definition sch_pin.cpp:708
Schematic symbol object.
Definition sch_symbol.h:75
Add cut/copy/paste, dark theme, autocomplete and brace highlighting to a wxStyleTextCtrl instance.
static constexpr auto MODEL_FIELD
static constexpr auto PIN_FIELD
static constexpr auto DIFF_FIELD
bool isPinDiff(const std::string &aComp, const std::string &aPinNumber) const
static constexpr auto LIBRARY_FIELD
Definition sim_library.h:31
static constexpr auto NAME_FIELD
Definition sim_library.h:32
bool SetIbisModel(const SIM_LIBRARY_IBIS &aLib, const std::string &aPinNumber, const std::string &aModelName)
Bind to a KIBIS model, set IO mode from its type, manage sw_state.
std::vector< std::pair< std::string, std::string > > GetIbisPins() const
void SwitchSingleEndedDiff(bool aDiff) override
bool CanDifferential() const
std::vector< std::string > GetIbisModels() const
bool ChangePin(const SIM_LIBRARY_IBIS &aLib, const std::string &aPinNumber)
update the list of available models based on the pin number.
std::string GetComponentName() const
static INFO TypeInfo(TYPE aType)
int GetPinCount() const
Definition sim_model.h:478
static bool InferSimModel(const wxString &aPrefix, std::span< const wxString > aPins, std::vector< SCH_FIELD > *aFields, bool aResolve, int aDepth, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
aPins follows lexical symbol-pin number order, including duplicate numbers.
const SPICE_GENERATOR & SpiceGenerator() const
Definition sim_model.h:429
virtual const PARAM & GetParam(unsigned aParamIndex) const
static TYPE ReadTypeFromFields(const std::vector< SCH_FIELD > &aFields, bool aResolve, int aDepth, REPORTER &aReporter)
int GetParamCount() const
Definition sim_model.h:488
void AssignSymbolPinNumberToModelPin(int aPinIndex, const wxString &aSymbolPinNumber)
DEVICE_INFO GetDeviceInfo() const
Definition sim_model.h:467
DEVICE_T GetDeviceType() const
Definition sim_model.h:470
static DEVICE_INFO DeviceInfo(DEVICE_T aDeviceType)
Definition sim_model.cpp:58
virtual bool HasAutofill() const
Definition sim_model.h:505
const SIM_MODEL_PIN & GetPin(unsigned aIndex) const
Definition sim_model.h:479
virtual bool HasPrimaryValue() const
Definition sim_model.h:506
TYPE GetType() const
Definition sim_model.h:471
static std::vector< wxString > PinNumbers(std::span< const SCH_PIN *const > aPins)
const SIM_MODEL::PARAM & GetParam() const
@ TYPE_FLOAT_VECTOR
Definition sim_value.h:71
@ TYPE_BOOL_VECTOR
Definition sim_value.h:69
@ TYPE_INT_VECTOR
Definition sim_value.h:70
@ TYPE_COMPLEX_VECTOR
Definition sim_value.h:72
@ TYPE_STRING
Definition sim_value.h:67
@ TYPE_COMPLEX
Definition sim_value.h:66
Special netlist exporter flavor that allows one to override simulation commands.
virtual std::string Preview(const SPICE_ITEM &aItem) const
A wrapper for reporting to a wxString object.
Definition reporter.h:242
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition confirm.cpp:217
This file is part of the common library.
static bool empty(const wxTextEntryBase *aCtrl)
bool equivalent(SIM_MODEL::DEVICE_T a, SIM_MODEL::DEVICE_T b)
#define FORCE_REFRESH_FROM_MODEL
SIM_MODEL::PARAM::CATEGORY CATEGORY
#define _(s)
@ NONE
Definition eda_fill.h:42
static const std::string KiCadUriPrefix
PROJECT & Prj()
Definition kicad.cpp:737
static bool loadLibrary(SYMBOL_LIBRARY_ADAPTER &aAdapter, const wxString &aNickname, std::vector< KI_ERROR > &aErrors)
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition wxgtk/ui.cpp:467
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:640
KICOMMON_API wxFont GetStatusFont(wxWindow *aWindow)
BITMAPS PinShapeGetBitmap(GRAPHIC_PINSHAPE shape)
Definition pin_type.cpp:246
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
void SetFieldValue(std::vector< SCH_FIELD > &aFields, const wxString &aFieldName, const std::string &aValue, bool aIsVisible=true, const SCH_SHEET_PATH *aSheetPath=nullptr, const wxString &aVariantName=wxEmptyString)
Definition sch_field.h:457
wxString GetFieldValue(const std::vector< SCH_FIELD > *aFields, FIELD_T aFieldType)
Definition sch_field.h:432
const SCH_FIELD * FindField(const std::vector< SCH_FIELD > &aFields, FIELD_T aFieldId)
Definition sch_field.h:394
#define SIM_PINS_FIELD
Definition sim_model.h:51
#define SIM_DEVICE_FIELD
Definition sim_model.h:49
#define SIM_REFERENCE_FIELD
Definition sim_model.h:46
#define SIM_PARAMS_FIELD
Definition sim_model.h:53
#define SIM_DECOMPOSITION_FIELD
Definition sim_model.h:52
#define SIM_DEVICE_SUBTYPE_FIELD
Definition sim_model.h:50
std::vector< FAB_LAYER_COLOR > dummy
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
Per-component decomposition descriptor stored in the Sim.Decomposition field.
static SIM_DECOMPOSITION Parse(const wxString &aField)
std::vector< wxString > sharedModelPins
wxString Format() const
std::vector< std::string > enumValues
Definition sim_model.h:386
SIM_VALUE::TYPE type
Definition sim_model.h:377
std::string defaultValue
Definition sim_model.h:380
std::string description
Definition sim_model.h:381
std::string value
Definition sim_model.h:398
const INFO & info
Definition sim_model.h:399
static constexpr auto NOT_CONNECTED
Definition sim_model.h:71
const std::string modelPinName
Definition sim_model.h:68
wxString symbolPinNumber
Definition sim_model.h:69
std::string modelName
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string subckt
std::string path
IbisParser parser & reporter
KIBIS_MODEL * model
KIBIS_PIN * pin
wxString result
Test unit parsing edge cases and error handling.