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