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 (C) 2022-2024 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, you may find one here:
20 * https://www.gnu.org/licenses/gpl-3.0.html
21 * or you may search the http://www.gnu.org website for the version 3 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27#include <dialog_sim_model.h>
28#include <sim/sim_property.h>
30#include <sim/sim_model.h>
31#include <sim/sim_model_kibis.h>
35#include <grid_tricks.h>
38#include <kiplatform/ui.h>
39#include <confirm.h>
40#include <string_utils.h>
41#include <locale_io.h>
42#include <wx/filedlg.h>
43#include <fmt/format.h>
44#include <sch_edit_frame.h>
47
49
50#define FORCE_UPDATE_PINS true
51
52
53bool equivalent( SIM_MODEL::DEVICE_T a, SIM_MODEL::DEVICE_T b )
54{
55 // A helper to handle SPICE's use of 'E' and 'H' for voltage sources and 'F' and 'G' for
56 // current sources
57 return a == b
58 || SIM_MODEL::DeviceInfo( a ).description == SIM_MODEL::DeviceInfo( b ).description;
59};
60
61
62template <typename T>
63DIALOG_SIM_MODEL<T>::DIALOG_SIM_MODEL( wxWindow* aParent, EDA_BASE_FRAME* aFrame, T& aSymbol,
64 std::vector<SCH_FIELD>& aFields ) :
65 DIALOG_SIM_MODEL_BASE( aParent ),
66 m_frame( aFrame ),
67 m_symbol( aSymbol ),
68 m_fields( aFields ),
69 m_libraryModelsMgr( &Prj() ),
70 m_builtinModelsMgr( &Prj() ),
71 m_prevModel( nullptr ),
72 m_curModelType( SIM_MODEL::TYPE::NONE ),
73 m_scintillaTricksCode( nullptr ),
74 m_scintillaTricksSubckt( nullptr ),
75 m_firstCategory( nullptr ),
76 m_prevParamGridSelection( nullptr ),
77 m_lastParamGridWidth( 0 ),
78 m_inKillFocus( false )
79{
80 m_browseButton->SetBitmap( KiBitmapBundle( BITMAPS::small_folder ) );
81
82 for( LIB_PIN* pin : aSymbol.GetAllLibPins() )
83 {
84 // De Morgan conversions are equivalences, not additional items to simulate
85 if( !pin->GetParentSymbol()->HasAlternateBodyStyle() || pin->GetBodyStyle() < 2 )
86 m_sortedPartPins.push_back( pin );
87 }
88
89 std::sort( m_sortedPartPins.begin(), m_sortedPartPins.end(),
90 []( const LIB_PIN* lhs, const LIB_PIN* rhs )
91 {
92 // We sort by StrNumCmp because SIM_MODEL_BASE sorts with it too.
93 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
94 } );
95
96 m_waveformChoice->Clear();
97 m_deviceChoice->Clear();
98 m_deviceSubtypeChoice->Clear();
99
100 m_scintillaTricksCode = new SCINTILLA_TRICKS( m_codePreview, wxT( "{}" ), false );
101 m_scintillaTricksSubckt = new SCINTILLA_TRICKS( m_subckt, wxT( "()" ), false );
102
103 m_paramGridMgr->Bind( wxEVT_PG_SELECTED, &DIALOG_SIM_MODEL::onParamGridSelectionChange, this );
104
105 wxPropertyGrid* grid = m_paramGrid->GetGrid();
106
107 // In wx 3.0 the color will be wrong sometimes.
108 grid->SetCellDisabledTextColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
109
110 grid->Bind( wxEVT_SET_FOCUS, &DIALOG_SIM_MODEL::onParamGridSetFocus, this );
111 grid->Bind( wxEVT_UPDATE_UI, &DIALOG_SIM_MODEL::onUpdateUI, this );
112
113 grid->DedicateKey( WXK_RETURN );
114 grid->DedicateKey( WXK_NUMPAD_ENTER );
115 grid->DedicateKey( WXK_UP );
116 grid->DedicateKey( WXK_DOWN );
117
118#if wxCHECK_VERSION( 3, 3, 0 )
119 grid->AddActionTrigger( wxPGKeyboardAction::Edit, WXK_RETURN );
120 grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_RETURN );
121 grid->AddActionTrigger( wxPGKeyboardAction::Edit, WXK_NUMPAD_ENTER );
122 grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_NUMPAD_ENTER );
123#else
124 grid->AddActionTrigger( wxPG_ACTION_EDIT, WXK_RETURN );
125 grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RETURN );
126 grid->AddActionTrigger( wxPG_ACTION_EDIT, WXK_NUMPAD_ENTER );
127 grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_NUMPAD_ENTER );
128#endif
129
131 m_pinAssignmentsGrid->PushEventHandler( new GRID_TRICKS( m_pinAssignmentsGrid ) );
132
134
135 // Now all widgets have the size fixed, call FinishDialogSettings
137}
138
139
140template <typename T>
142{
143 // Disable all properties. This is necessary because some of their methods are called after
144 // destruction of DIALOG_SIM_MODEL, oddly. When disabled, they never access their models.
145 for( wxPropertyGridIterator it = m_paramGrid->GetIterator(); !it.AtEnd(); ++it )
146 {
147 SIM_PROPERTY* prop = dynamic_cast<SIM_PROPERTY*>( *it );
148
149 if( !prop )
150 continue;
151
152 prop->Disable();
153 }
154
155 // Delete the GRID_TRICKS.
156 m_pinAssignmentsGrid->PopEventHandler( true );
157
158 delete m_scintillaTricksCode;
159 delete m_scintillaTricksSubckt;
160}
161
162
163template <typename T>
165{
166 wxCommandEvent dummyEvent;
167 wxString deviceType;
168 wxString modelType;
169 wxString modelParams;
170 wxString pinMap;
171 bool storeInValue = false;
172
173 wxString msg;
174 WX_STRING_REPORTER reporter( &msg );
175
176 auto setFieldValue =
177 [&]( const wxString& aFieldName, const wxString& aValue )
178 {
179 for( SCH_FIELD& field : m_fields )
180 {
181 if( field.GetName() == aFieldName )
182 {
183 field.SetText( aValue );
184 return;
185 }
186 }
187
188 m_fields.emplace_back( &m_symbol, -1, aFieldName );
189 m_fields.back().SetText( aValue );
190 };
191
192 // Infer RLC and VI models if they aren't specified
193 if( SIM_MODEL::InferSimModel( m_symbol, &m_fields, false, SIM_VALUE_GRAMMAR::NOTATION::SI,
194 &deviceType, &modelType, &modelParams, &pinMap ) )
195 {
196 setFieldValue( SIM_DEVICE_FIELD, deviceType );
197
198 if( !modelType.IsEmpty() )
199 setFieldValue( SIM_DEVICE_SUBTYPE_FIELD, modelType );
200
201 setFieldValue( SIM_PARAMS_FIELD, modelParams );
202
203 setFieldValue( SIM_PINS_FIELD, pinMap );
204
205 storeInValue = true;
206
207 // In case the storeInValue checkbox is turned off (if it's left on then we'll overwrite
208 // this field with the actual value):
209 m_fields[ VALUE_FIELD ].SetText( wxT( "${SIM.PARAMS}" ) );
210 }
211
212 std::string libraryFilename = SIM_MODEL::GetFieldValue( &m_fields, SIM_LIBRARY::LIBRARY_FIELD );
213
214 if( libraryFilename != "" )
215 {
216 // The model is sourced from a library, optionally with instance overrides.
217 m_rbLibraryModel->SetValue( true );
218
219 if( !loadLibrary( libraryFilename ) )
220 {
221 m_libraryPathText->ChangeValue( libraryFilename );
222 m_curModelType = SIM_MODEL::ReadTypeFromFields( m_fields, reporter );
223
224 // load library will mangle the set reporter
225 m_libraryModelsMgr.CreateModel( nullptr, m_sortedPartPins, m_fields, reporter );
226
227 m_modelNameChoice->Append( _( "<unknown>" ) );
228 m_modelNameChoice->SetSelection( 0 );
229 }
230 else
231 {
232 std::string modelName = SIM_MODEL::GetFieldValue( &m_fields, SIM_LIBRARY::NAME_FIELD );
233 int modelIdx = m_modelNameChoice->FindString( modelName );
234
235 if( modelIdx == wxNOT_FOUND )
236 {
237 DisplayErrorMessage( this, wxString::Format( _( "No model named '%s' in library." ),
238 modelName ) );
239
240 // Default to first item in library
241 m_modelNameChoice->SetSelection( 0 );
242 }
243 else
244 {
245 m_modelNameChoice->SetSelection( modelIdx );
246 }
247
248 m_curModelType = curModel().GetType();
249 }
250
251 if( isIbisLoaded() && ( m_modelNameChoice->GetSelection() >= 0 ) )
252 {
253 int idx = m_modelNameChoice->GetSelection();
254 auto kibismodel = dynamic_cast<SIM_MODEL_KIBIS*>( &m_libraryModelsMgr.GetModels()[idx].get() );
255
256 if( kibismodel )
257 {
258 onModelNameChoice( dummyEvent ); // refresh list of pins
259
260 int i = 0;
261
262 for( const std::pair<std::string, std::string>& strs : kibismodel->GetIbisPins() )
263 {
264 if( strs.first == SIM_MODEL::GetFieldValue( &m_fields, SIM_LIBRARY_KIBIS::PIN_FIELD ) )
265 {
266 auto kibisLibrary = static_cast<const SIM_LIBRARY_KIBIS*>( library() );
267
268 kibismodel->ChangePin( *kibisLibrary, strs.first );
269 m_pinCombobox->SetSelection( static_cast<int>( i ) );
270 break;
271 }
272 i++;
273 }
274
275 if( i < static_cast<int>( kibismodel->GetIbisPins().size() ) )
276 {
277 onPinCombobox( dummyEvent ); // refresh list of models
278
279 m_pinModelCombobox->SetStringSelection(
281 }
282
284 {
285 kibismodel->SwitchSingleEndedDiff( true );
286 m_differentialCheckbox->SetValue( true );
287 }
288 else
289 {
290 kibismodel->SwitchSingleEndedDiff( false );
291 m_differentialCheckbox->SetValue( false );
292 }
293 }
294 }
295 }
296 else if( !SIM_MODEL::GetFieldValue( &m_fields, SIM_DEVICE_FIELD ).empty()
298 {
299 // The model is sourced from the instance.
300 m_rbBuiltinModel->SetValue( true );
301
302 msg.clear();
303 m_curModelType = SIM_MODEL::ReadTypeFromFields( m_fields, reporter );
304
305 if( reporter.HasMessage() )
306 DisplayErrorMessage( this, msg );
307 }
308
309 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
310 {
311 if( m_rbBuiltinModel->GetValue() && type == m_curModelType )
312 {
313 msg.clear();
314 m_builtinModelsMgr.CreateModel( m_fields, m_sortedPartPins, false, reporter );
315
316 if( reporter.HasMessage() )
317 {
318 DisplayErrorMessage( this, _( "Failed to read simulation model from fields." )
319 + wxT( "\n\n" ) + msg );
320 }
321 }
322 else
323 {
324 m_builtinModelsMgr.CreateModel( type, m_sortedPartPins, reporter );
325 }
326
327 SIM_MODEL::DEVICE_T deviceTypeT = SIM_MODEL::TypeInfo( type ).deviceType;
328
329 if( !m_curModelTypeOfDeviceType.count( deviceTypeT ) )
330 m_curModelTypeOfDeviceType[deviceTypeT] = type;
331 }
332
333 if( storeInValue )
334 curModel().SetIsStoredInValue( true );
335
336 m_saveInValueCheckbox->SetValue( curModel().IsStoredInValue() );
337
338 onRadioButton( dummyEvent );
339 return DIALOG_SIM_MODEL_BASE::TransferDataToWindow();
340}
341
342
343template <typename T>
345{
346 m_pinAssignmentsGrid->CommitPendingChanges();
347 m_paramGrid->GetGrid()->CommitChangesFromEditor();
348
349 if( !DIALOG_SIM_MODEL_BASE::TransferDataFromWindow() )
350 return false;
351
352 SIM_MODEL& model = curModel();
353 std::string path;
354 std::string name;
355
356 if( m_rbLibraryModel->GetValue() )
357 {
358 path = m_libraryPathText->GetValue();
359 wxFileName fn( path );
360
361 if( fn.MakeRelativeTo( Prj().GetProjectPath() ) && !fn.GetFullPath().StartsWith( ".." ) )
362 path = fn.GetFullPath();
363
364 if( !m_modelNameChoice->IsEmpty() )
365 name = m_modelNameChoice->GetStringSelection().ToStdString();
366 else if( dynamic_cast<SIM_MODEL_SPICE_FALLBACK*>( &model ) )
368 }
369
372
373 if( isIbisLoaded() )
374 {
375 SIM_MODEL_KIBIS* ibismodel = static_cast<SIM_MODEL_KIBIS*>(
376 &m_libraryModelsMgr.GetModels().at( m_modelNameChoice->GetSelection() ).get() );
377
378 if( ibismodel )
379 {
380 std::string pins;
381 std::string modelName = std::string( m_pinModelCombobox->GetValue().c_str() );
382 std::string differential;
383
384 if( m_pinCombobox->GetSelection() >= 0 )
385 pins = ibismodel->GetIbisPins().at( m_pinCombobox->GetSelection() ).first;
386
387 if( ibismodel->CanDifferential() && m_differentialCheckbox->GetValue() )
388 differential = "1";
389
393 }
394 }
395
396 if( model.GetType() == SIM_MODEL::TYPE::RAWSPICE )
397 {
398 if( m_modelNotebook->GetSelection() == 0 )
399 updateModelCodeTab( &model );
400
401 wxString code = m_codePreview->GetText().Trim( true ).Trim( false );
402 model.SetParamValue( "model", std::string( code.ToUTF8() ) );
403 }
404
405 model.SetIsStoredInValue( m_saveInValueCheckbox->GetValue() );
406
407 for( int row = 0; row < m_pinAssignmentsGrid->GetNumberRows(); ++row )
408 {
409 wxString modelPinName = m_pinAssignmentsGrid->GetCellValue( row, PIN_COLUMN::MODEL );
410 wxString symbolPinName = m_sortedPartPins.at( row )->GetShownNumber();
411
412 model.SetPinSymbolPinNumber( getModelPinIndex( modelPinName ),
413 std::string( symbolPinName.ToUTF8() ) );
414 }
415
416 removeOrphanedPinAssignments( &model );
417
418 curModel().WriteFields( m_fields );
419
420 return true;
421}
422
423
424template <typename T>
426{
427 // always enable the library browser button -- it makes for fewer clicks if the user has a
428 // whole bunch of inferred passives that they want to specify library models for
429 m_browseButton->Enable();
430
431 // if we're in an undetermined state then enable everything for faster access
432 bool undetermined = !m_rbLibraryModel->GetValue() && !m_rbBuiltinModel->GetValue();
433 bool enableLibCtrls = m_rbLibraryModel->GetValue() || undetermined;
434 bool enableBuiltinCtrls = m_rbBuiltinModel->GetValue() || undetermined;
435
436 m_pathLabel->Enable( enableLibCtrls );
437 m_libraryPathText->Enable( enableLibCtrls );
438 m_modelNameLabel->Enable( enableLibCtrls );
439 m_modelNameChoice->Enable( enableLibCtrls );
440 m_pinLabel->Enable( enableLibCtrls );
441 m_pinCombobox->Enable( enableLibCtrls );
442 m_differentialCheckbox->Enable( enableLibCtrls );
443 m_pinModelLabel->Enable( enableLibCtrls );
444 m_pinModelCombobox->Enable( enableLibCtrls );
445 m_waveformLabel->Enable( enableLibCtrls );
446 m_waveformChoice->Enable( enableLibCtrls );
447
448 m_deviceLabel->Enable( enableBuiltinCtrls );
449 m_deviceChoice->Enable( enableBuiltinCtrls );
450 m_deviceSubtypeLabel->Enable( enableBuiltinCtrls );
451 m_deviceSubtypeChoice->Enable( enableBuiltinCtrls );
452
453 SIM_MODEL* model = &curModel();
454
455 updateIbisWidgets( model );
456 updateBuiltinModelWidgets( model );
457 updateModelParamsTab( model );
458 updateModelCodeTab( model );
459 updatePinAssignments( model, false );
460
461 std::string ref = SIM_MODEL::GetFieldValue( &m_fields, SIM_REFERENCE_FIELD );
462
463 m_modelPanel->Layout();
464 m_pinAssignmentsPanel->Layout();
465 m_parametersPanel->Layout();
466 m_codePanel->Layout();
467
468 SendSizeEvent( wxSEND_EVENT_POST );
469
470 m_prevModel = &curModel();
471}
472
473
474template <typename T>
476{
477 SIM_MODEL_KIBIS* modelkibis = isIbisLoaded() ? dynamic_cast<SIM_MODEL_KIBIS*>( aModel )
478 : nullptr;
479
480 m_pinLabel->Show( isIbisLoaded() );
481 m_pinCombobox->Show( isIbisLoaded() );
482 m_pinModelLabel->Show( isIbisLoaded() );
483 m_pinModelCombobox->Show( isIbisLoaded() );
484 m_waveformLabel->Show( isIbisLoaded() );
485 m_waveformChoice->Show( isIbisLoaded() );
486
487 if( aModel != m_prevModel )
488 {
489 m_waveformChoice->Clear();
490
491 if( isIbisLoaded() )
492 {
493 for( SIM_MODEL::TYPE type : { SIM_MODEL::TYPE::KIBIS_DEVICE,
494 SIM_MODEL::TYPE::KIBIS_DRIVER_DC,
495 SIM_MODEL::TYPE::KIBIS_DRIVER_RECT,
496 SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS } )
497 {
498 SIM_MODEL::DEVICE_T deviceType = SIM_MODEL::TypeInfo( type ).deviceType;
499 const std::string& deviceTypeDesc = SIM_MODEL::DeviceInfo( deviceType ).description;
500
501 if( deviceType == aModel->GetDeviceType()
502 || deviceTypeDesc == aModel->GetDeviceInfo().description )
503 {
504 m_waveformChoice->Append( SIM_MODEL::TypeInfo( type ).description );
505
506 if( type == aModel->GetType() )
507 m_waveformChoice->SetSelection( m_waveformChoice->GetCount() - 1 );
508 }
509 }
510 }
511 }
512
513 m_differentialCheckbox->Show( isIbisLoaded() && modelkibis && modelkibis->CanDifferential() );
514 m_modelNameLabel->SetLabel( isIbisLoaded() ? _( "Component:" ) : _( "Model:" ) );
515}
516
517
518template <typename T>
520{
521 // Change the Type choice to match the current device type.
522 if( aModel != m_prevModel )
523 {
524 m_deviceChoice->Clear();
525 m_deviceSubtypeChoice->Clear();
526
527 if( !m_rbLibraryModel->GetValue() )
528 {
529 for( SIM_MODEL::DEVICE_T deviceType : SIM_MODEL::DEVICE_T_ITERATOR() )
530 {
531 if( !SIM_MODEL::DeviceInfo( deviceType ).showInMenu )
532 continue;
533
534 m_deviceChoice->Append( SIM_MODEL::DeviceInfo( deviceType ).description );
535
536 if( equivalent( deviceType, aModel->GetDeviceType() ) )
537 m_deviceChoice->SetSelection( m_deviceChoice->GetCount() - 1 );
538 }
539
540 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
541 {
542 if( type == SIM_MODEL::TYPE::KIBIS_DEVICE
543 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_DC
544 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_RECT
545 || type == SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS )
546 {
547 continue;
548 }
549
550 SIM_MODEL::DEVICE_T deviceType = SIM_MODEL::TypeInfo( type ).deviceType;
551 const std::string& deviceTypeDesc = SIM_MODEL::DeviceInfo( deviceType ).description;
552
553 if( deviceType == aModel->GetDeviceType()
554 || deviceTypeDesc == aModel->GetDeviceInfo().description )
555 {
556 m_deviceSubtypeChoice->Append( SIM_MODEL::TypeInfo( type ).description );
557
558 if( type == aModel->GetType() )
559 m_deviceSubtypeChoice->SetSelection( m_deviceSubtypeChoice->GetCount() - 1 );
560 }
561 }
562 }
563
564 m_deviceSubtypeLabel->Show( m_deviceSubtypeChoice->GetCount() > 1 );
565 m_deviceSubtypeChoice->Show( m_deviceSubtypeChoice->GetCount() > 1 );
566 }
567
568 if( dynamic_cast<SIM_MODEL_RAW_SPICE*>( aModel ) )
569 m_modelNotebook->SetSelection( 1 );
570 else
571 m_modelNotebook->SetSelection( 0 );
572
573 if( aModel->HasPrimaryValue() )
574 {
575 const SIM_MODEL::PARAM& primary = aModel->GetParam( 0 );
576
577 m_saveInValueCheckbox->SetLabel( wxString::Format( _( "Save parameter '%s (%s)' in Value "
578 "field" ),
579 primary.info.description,
580 primary.info.name ) );
581 m_saveInValueCheckbox->Enable( true );
582 }
583 else
584 {
585 m_saveInValueCheckbox->SetLabel( _( "Save primary parameter in Value field" ) );
586 m_saveInValueCheckbox->SetValue( false );
587 m_saveInValueCheckbox->Enable( false );
588 }
589}
590
591
592template <typename T>
594{
595 if( aModel != m_prevModel )
596 {
597 // This wxPropertyGridManager column and header stuff has to be here because it segfaults in
598 // the constructor.
599
600 m_paramGridMgr->SetColumnCount( PARAM_COLUMN::END_ );
601
602 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::DESCRIPTION, _( "Parameter" ) );
603 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::UNIT, _( "Unit" ) );
604 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::DEFAULT, _( "Default" ) );
605 m_paramGridMgr->SetColumnTitle( PARAM_COLUMN::TYPE, _( "Type" ) );
606
607 m_paramGridMgr->ShowHeader();
608
609
610 m_paramGrid->Clear();
611
612 m_firstCategory = m_paramGrid->Append( new wxPropertyCategory( "Geometry" ) );
613 m_paramGrid->HideProperty( "Geometry" );
614
615 m_paramGrid->Append( new wxPropertyCategory( "AC" ) );
616 m_paramGrid->HideProperty( "AC" );
617
618 m_paramGrid->Append( new wxPropertyCategory( "DC" ) );
619 m_paramGrid->HideProperty( "DC" );
620
621 m_paramGrid->Append( new wxPropertyCategory( "S-Parameters" ) );
622 m_paramGrid->HideProperty( "S-Parameters" );
623
624 m_paramGrid->Append( new wxPropertyCategory( "Capacitance" ) );
625 m_paramGrid->HideProperty( "Capacitance" );
626
627 m_paramGrid->Append( new wxPropertyCategory( "Temperature" ) );
628 m_paramGrid->HideProperty( "Temperature" );
629
630 m_paramGrid->Append( new wxPropertyCategory( "Noise" ) );
631 m_paramGrid->HideProperty( "Noise" );
632
633 m_paramGrid->Append( new wxPropertyCategory( "Distributed Quantities" ) );
634 m_paramGrid->HideProperty( "Distributed Quantities" );
635
636 m_paramGrid->Append( new wxPropertyCategory( "Waveform" ) );
637 m_paramGrid->HideProperty( "Waveform" );
638
639 m_paramGrid->Append( new wxPropertyCategory( "Limiting Values" ) );
640 m_paramGrid->HideProperty( "Limiting Values" );
641
642 m_paramGrid->Append( new wxPropertyCategory( "Advanced" ) );
643 m_paramGrid->HideProperty( "Advanced" );
644
645 m_paramGrid->Append( new wxPropertyCategory( "Flags" ) );
646 m_paramGrid->HideProperty( "Flags" );
647
648 m_paramGrid->CollapseAll();
649
650 for( int i = 0; i < aModel->GetParamCount(); ++i )
651 addParamPropertyIfRelevant( aModel, i );
652
653 m_paramGrid->CollapseAll();
654 m_paramGrid->Expand( "AC" );
655 m_paramGrid->Expand( "Waveform" );
656 }
657
658 adjustParamGridColumns( m_paramGrid->GetGrid()->GetSize().GetX(), true );
659
660 // Set all properties to default colors.
661 // Update properties in models that have autofill.
662 for( wxPropertyGridIterator it = m_paramGrid->GetIterator(); !it.AtEnd(); ++it )
663 {
664 wxColour bgCol = m_paramGrid->GetGrid()->GetPropertyDefaultCell().GetBgCol();
665 wxColour fgCol = m_paramGrid->GetGrid()->GetPropertyDefaultCell().GetFgCol();
666
667 for( int col = 0; col < m_paramGridMgr->GetColumnCount(); ++col )
668 {
669 ( *it )->GetCell( col ).SetBgCol( bgCol );
670 ( *it )->GetCell( col ).SetFgCol( fgCol );
671 }
672
673 SIM_PROPERTY* prop = dynamic_cast<SIM_PROPERTY*>( *it );
674
675 if( !prop )
676 continue;
677
678 const SIM_MODEL::PARAM& param = prop->GetParam();
679
680 // Model values other than the currently edited value may have changed. Update them.
681 // This feature is called "autofill" and present only in certain models. Don't do it for
682 // models that don't have it for performance reasons.
683 if( aModel->HasAutofill() )
684 ( *it )->SetValueFromString( param.value );
685 }
686}
687
688
689template <typename T>
691{
692 if( dynamic_cast<SIM_MODEL_SPICE_FALLBACK*>( aModel ) )
693 return;
694
695 wxString text;
696 SPICE_ITEM item;
697
698 item.modelName = m_modelNameChoice->GetStringSelection();
699
700 if( m_rbBuiltinModel->GetValue() || item.modelName == "" )
701 item.modelName = m_fields.at( REFERENCE_FIELD ).GetText();
702
703 text << aModel->SpiceGenerator().Preview( item );
704
705 m_codePreview->SetText( text );
706 m_codePreview->SelectNone();
707}
708
709
710template <typename T>
711void DIALOG_SIM_MODEL<T>::updatePinAssignments( SIM_MODEL* aModel, bool aForceUpdatePins )
712{
713 if( m_pinAssignmentsGrid->GetNumberRows() == 0 )
714 {
715 m_pinAssignmentsGrid->AppendRows( static_cast<int>( m_sortedPartPins.size() ) );
716
717 for( int ii = 0; ii < m_pinAssignmentsGrid->GetNumberRows(); ++ii )
718 {
719 wxString symbolPinString = getSymbolPinString( ii );
720
721 m_pinAssignmentsGrid->SetReadOnly( ii, PIN_COLUMN::SYMBOL );
722 m_pinAssignmentsGrid->SetCellValue( ii, PIN_COLUMN::SYMBOL, symbolPinString );
723 }
724
725 aForceUpdatePins = true;
726 }
727
728 if( aForceUpdatePins )
729 {
730 // Reset the grid.
731 for( int row = 0; row < m_pinAssignmentsGrid->GetNumberRows(); ++row )
732 m_pinAssignmentsGrid->SetCellValue( row, PIN_COLUMN::MODEL, _( "Not Connected" ) );
733
734 // Now set up the grid values in the Model column.
735 for( int modelPinIndex = 0; modelPinIndex < aModel->GetPinCount(); ++modelPinIndex )
736 {
737 wxString symbolPinNumber = aModel->GetPin( modelPinIndex ).symbolPinNumber;
738
739 if( symbolPinNumber == "" )
740 continue;
741
742 int symbolPinRow = findSymbolPinRow( symbolPinNumber );
743
744 if( symbolPinRow == -1 )
745 continue;
746
747 wxString modelPinString = getModelPinString( aModel, modelPinIndex );
748 m_pinAssignmentsGrid->SetCellValue( symbolPinRow, PIN_COLUMN::MODEL, modelPinString );
749 }
750 }
751
752 for( int ii = 0; ii < m_pinAssignmentsGrid->GetNumberRows(); ++ii )
753 {
754 // Set up the Model column cell editors with dropdown options.
755 std::vector<BITMAPS> modelPinIcons;
756 wxArrayString modelPinChoices;
757
758 for( int jj = 0; jj < aModel->GetPinCount(); ++jj )
759 {
760 if( aModel->GetPin( jj ).symbolPinNumber != "" )
761 modelPinIcons.push_back( PinShapeGetBitmap( GRAPHIC_PINSHAPE::LINE ) );
762 else
763 modelPinIcons.push_back( BITMAPS::INVALID_BITMAP );
764
765 modelPinChoices.Add( getModelPinString( aModel, jj ) );
766 }
767
768 modelPinIcons.push_back( BITMAPS::INVALID_BITMAP );
769 modelPinChoices.Add( _( "Not Connected" ) );
770
771 // Using `new` here shouldn't cause a memory leak because `SetCellEditor()` calls
772 // `DecRef()` on its last editor.
773 m_pinAssignmentsGrid->SetCellEditor( ii, PIN_COLUMN::MODEL,
774 new GRID_CELL_ICON_TEXT_POPUP( modelPinIcons,
775 modelPinChoices ) );
776 }
777
778 // TODO: Show a preview of the symbol with the pin numbers shown.
779
780 if( aModel->GetType() == SIM_MODEL::TYPE::SUBCKT )
781 {
782 SIM_MODEL_SUBCKT* subckt = static_cast<SIM_MODEL_SUBCKT*>( aModel );
783 m_subckt->SetText( subckt->GetSpiceCode() );
784 m_subckt->SetEditable( false );
785 }
786 else
787 {
788 m_subcktLabel->Show( false );
789 m_subckt->Show( false );
790 }
791}
792
793
794template <typename T>
796{
797 for( int i = 0; i < aModel->GetPinCount(); ++i )
798 {
799 if( !m_symbol.GetPin( aModel->GetPin( i ).symbolPinNumber ) )
800 aModel->SetPinSymbolPinNumber( i, "" );
801 }
802}
803
804
805template <typename T>
806bool DIALOG_SIM_MODEL<T>::loadLibrary( const wxString& aLibraryPath, bool aForceReload )
807{
808 if( m_prevLibrary == aLibraryPath && !aForceReload )
809 return true;
810
811 wxString msg;
812 WX_STRING_REPORTER reporter( &msg );
813
814 m_libraryModelsMgr.SetForceFullParse();
815 m_libraryModelsMgr.SetLibrary( aLibraryPath, reporter );
816
817 if( reporter.HasMessage() )
818 {
819 DisplayErrorMessage( this, msg );
820 return false;
821 }
822
823 std::string modelName = SIM_MODEL::GetFieldValue( &m_fields, SIM_LIBRARY::NAME_FIELD );
824
825 for( const auto& [baseModelName, baseModel] : library()->GetModels() )
826 {
827 if( baseModelName == modelName )
828 m_libraryModelsMgr.CreateModel( &baseModel, m_sortedPartPins, m_fields, reporter );
829 else
830 m_libraryModelsMgr.CreateModel( &baseModel, m_sortedPartPins, reporter );
831 }
832
833 if( reporter.HasMessage() )
834 DisplayErrorMessage( this, msg );
835
836 m_rbLibraryModel->SetValue( true );
837 m_libraryPathText->ChangeValue( aLibraryPath );
838
839 wxArrayString modelNames;
840
841 for( const auto& [name, model] : library()->GetModels() )
842 modelNames.Add( name );
843
844 m_modelNameChoice->Clear();
845 m_modelNameChoice->Append( modelNames );
846
847 if( isIbisLoaded() )
848 {
849 wxArrayString emptyArray;
850 m_pinModelCombobox->Set( emptyArray );
851 m_pinCombobox->Set( emptyArray );
852 m_pinModelCombobox->SetSelection( -1 );
853 m_pinCombobox->SetSelection( -1 );
854 }
855
856 m_prevLibrary = aLibraryPath;
857 return true;
858}
859
860
861template <typename T>
863{
864 if( aModel->GetParam( aParamIndex ).info.dir == SIM_MODEL::PARAM::DIR_OUT )
865 return;
866
867 switch( aModel->GetParam( aParamIndex ).info.category )
868 {
869 case CATEGORY::AC:
870 m_paramGrid->HideProperty( "AC", false );
871 m_paramGrid->AppendIn( "AC", newParamProperty( aModel, aParamIndex ) );
872 break;
873
874 case CATEGORY::DC:
875 m_paramGrid->HideProperty( "DC", false );
876 m_paramGrid->AppendIn( "DC", newParamProperty( aModel, aParamIndex ) );
877 break;
878
879 case CATEGORY::S_PARAM:
880 m_paramGrid->HideProperty( "S-Parameters", false );
881 m_paramGrid->AppendIn( "S-Parameters", newParamProperty( aModel, aParamIndex ) );
882 break;
883
884 case CATEGORY::CAPACITANCE:
885 m_paramGrid->HideProperty( "Capacitance", false );
886 m_paramGrid->AppendIn( "Capacitance", newParamProperty( aModel, aParamIndex ) );
887 break;
888
889 case CATEGORY::TEMPERATURE:
890 m_paramGrid->HideProperty( "Temperature", false );
891 m_paramGrid->AppendIn( "Temperature", newParamProperty( aModel, aParamIndex ) );
892 break;
893
894 case CATEGORY::NOISE:
895 m_paramGrid->HideProperty( "Noise", false );
896 m_paramGrid->AppendIn( "Noise", newParamProperty( aModel, aParamIndex ) );
897 break;
898
899 case CATEGORY::DISTRIBUTED_QUANTITIES:
900 m_paramGrid->HideProperty( "Distributed Quantities", false );
901 m_paramGrid->AppendIn( "Distributed Quantities", newParamProperty( aModel, aParamIndex ) );
902 break;
903
904 case CATEGORY::WAVEFORM:
905 m_paramGrid->HideProperty( "Waveform", false );
906 m_paramGrid->AppendIn( "Waveform", newParamProperty( aModel, aParamIndex ) );
907 break;
908
909 case CATEGORY::GEOMETRY:
910 m_paramGrid->HideProperty( "Geometry", false );
911 m_paramGrid->AppendIn( "Geometry", newParamProperty( aModel, aParamIndex ) );
912 break;
913
914 case CATEGORY::LIMITING_VALUES:
915 m_paramGrid->HideProperty( "Limiting Values", false );
916 m_paramGrid->AppendIn( "Limiting Values", newParamProperty( aModel, aParamIndex ) );
917 break;
918
919 case CATEGORY::ADVANCED:
920 m_paramGrid->HideProperty( "Advanced", false );
921 m_paramGrid->AppendIn( "Advanced", newParamProperty( aModel, aParamIndex ) );
922 break;
923
924 case CATEGORY::FLAGS:
925 m_paramGrid->HideProperty( "Flags", false );
926 m_paramGrid->AppendIn( "Flags", newParamProperty( aModel, aParamIndex ) );
927 break;
928
929 default:
930 m_paramGrid->Insert( m_firstCategory, newParamProperty( aModel, aParamIndex ) );
931 break;
932
933 case CATEGORY::INITIAL_CONDITIONS:
934 case CATEGORY::SUPERFLUOUS:
935 return;
936 }
937}
938
939
940template <typename T>
941wxPGProperty* DIALOG_SIM_MODEL<T>::newParamProperty( SIM_MODEL* aModel, int aParamIndex ) const
942{
943 const SIM_MODEL::PARAM& param = aModel->GetParam( aParamIndex );
944 wxString paramDescription;
945
946 if( param.info.description == "" )
947 paramDescription = wxString::Format( "%s", param.info.name );
948 else
949 paramDescription = wxString::Format( "%s (%s)", param.info.description, param.info.name );
950
951 wxPGProperty* prop = nullptr;
952
953 switch( param.info.type )
954 {
956 // TODO.
957 prop = new SIM_BOOL_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex );
958 prop->SetAttribute( wxPG_BOOL_USE_CHECKBOX, true );
959 break;
960
962 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
964 break;
965
967 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
969 break;
970
971 //case TYPE_COMPLEX:
972 // break;
973
975 // Special case: K-line mutual inductance statement parameters l1 and l2 are references
976 // to other inductors in the circuit.
977 if( dynamic_cast<SIM_MODEL_L_MUTUAL*>( aModel ) != nullptr
978 && ( param.info.name == "l1" || param.info.name == "l2" ) )
979 {
980 wxArrayString inductors;
981
982 if( SCH_EDIT_FRAME* schEditFrame = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
983 {
984 SPICE_CIRCUIT_MODEL circuit( &schEditFrame->Schematic() );
985 NULL_REPORTER devNul;
986
988 devNul );
989
990 for( const SPICE_ITEM& item : circuit.GetItems() )
991 {
992 if( item.model->GetDeviceType() == SIM_MODEL::DEVICE_T::L )
993 inductors.push_back( item.refName );
994 }
995
996 inductors.Sort(
997 []( const wxString& a, const wxString& b ) -> int
998 {
999 return StrNumCmp( a, b, true );
1000 } );
1001 }
1002
1003 if( inductors.empty() )
1004 {
1005 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel,
1006 aParamIndex, SIM_VALUE::TYPE_STRING );
1007 }
1008 else
1009 {
1010 prop = new SIM_ENUM_PROPERTY( paramDescription, param.info.name, *aModel,
1011 aParamIndex, inductors );
1012 }
1013 }
1014 else if( param.info.enumValues.empty() )
1015 {
1016 prop = new SIM_STRING_PROPERTY( paramDescription, param.info.name, *aModel,
1017 aParamIndex, SIM_VALUE::TYPE_STRING );
1018 }
1019 else
1020 {
1021 wxArrayString values;
1022
1023 for( const std::string& string : aModel->GetParam( aParamIndex ).info.enumValues )
1024 values.Add( string );
1025
1026 prop = new SIM_ENUM_PROPERTY( paramDescription, param.info.name, *aModel, aParamIndex,
1027 values );
1028 }
1029 break;
1030
1031 default:
1032 prop = new wxStringProperty( paramDescription, param.info.name );
1033 break;
1034 }
1035
1036 prop->SetAttribute( wxPG_ATTR_UNITS, wxString::FromUTF8( param.info.unit.c_str() ) );
1037
1038 // Legacy due to the way we extracted the parameters from Ngspice.
1039 prop->SetCell( 3, wxString::FromUTF8( param.info.defaultValue ) );
1040
1041 wxString typeStr;
1042
1043 switch( param.info.type )
1044 {
1045 case SIM_VALUE::TYPE_BOOL: typeStr = wxT( "Bool" ); break;
1046 case SIM_VALUE::TYPE_INT: typeStr = wxT( "Int" ); break;
1047 case SIM_VALUE::TYPE_FLOAT: typeStr = wxT( "Float" ); break;
1048 case SIM_VALUE::TYPE_COMPLEX: typeStr = wxT( "Complex" ); break;
1049 case SIM_VALUE::TYPE_STRING: typeStr = wxT( "String" ); break;
1050 case SIM_VALUE::TYPE_BOOL_VECTOR: typeStr = wxT( "Bool Vector" ); break;
1051 case SIM_VALUE::TYPE_INT_VECTOR: typeStr = wxT( "Int Vector" ); break;
1052 case SIM_VALUE::TYPE_FLOAT_VECTOR: typeStr = wxT( "Float Vector" ); break;
1053 case SIM_VALUE::TYPE_COMPLEX_VECTOR: typeStr = wxT( "Complex Vector" ); break;
1054 }
1055
1056 prop->SetCell( PARAM_COLUMN::TYPE, typeStr );
1057
1058 return prop;
1059}
1060
1061
1062template <typename T>
1063int DIALOG_SIM_MODEL<T>::findSymbolPinRow( const wxString& aSymbolPinNumber ) const
1064{
1065 for( int row = 0; row < static_cast<int>( m_sortedPartPins.size() ); ++row )
1066 {
1067 LIB_PIN* pin = m_sortedPartPins[row];
1068
1069 if( pin->GetNumber() == aSymbolPinNumber )
1070 return row;
1071 }
1072
1073 return -1;
1074}
1075
1076
1077template <typename T>
1079{
1080 if( m_rbLibraryModel->GetValue() )
1081 {
1082 int sel = m_modelNameChoice->GetSelection();
1083
1084 if( sel >= 0 && sel < (int) m_libraryModelsMgr.GetModels().size() )
1085 return m_libraryModelsMgr.GetModels().at( sel ).get();
1086 }
1087 else
1088 {
1089 if( (int) m_curModelType < (int) m_builtinModelsMgr.GetModels().size() )
1090 return m_builtinModelsMgr.GetModels().at( (int) m_curModelType );
1091 }
1092
1093 return m_builtinModelsMgr.GetModels().at( (int) SIM_MODEL::TYPE::NONE );
1094}
1095
1096
1097template <typename T>
1099{
1100 if( m_libraryModelsMgr.GetLibraries().size() == 1 )
1101 return &m_libraryModelsMgr.GetLibraries().begin()->second.get();
1102
1103 return nullptr;
1104}
1105
1106
1107template <typename T>
1108wxString DIALOG_SIM_MODEL<T>::getSymbolPinString( int symbolPinIndex ) const
1109{
1110 LIB_PIN* pin = m_sortedPartPins.at( symbolPinIndex );
1111 wxString pinNumber;
1112 wxString pinName;
1113
1114 if( pin )
1115 {
1116 pinNumber = pin->GetShownNumber();
1117 pinName = pin->GetShownName();
1118 }
1119
1120 if( !pinName.IsEmpty() && pinName != pinNumber )
1121 pinNumber += wxString::Format( wxT( " (%s)" ), pinName );
1122
1123 return pinNumber;
1124}
1125
1126
1127template <typename T>
1128wxString DIALOG_SIM_MODEL<T>::getModelPinString( SIM_MODEL* aModel, int aModelPinIndex ) const
1129{
1130 const wxString& pinName = aModel->GetPin( aModelPinIndex ).name;
1131
1132 LOCALE_IO toggle;
1133
1134 wxString pinNumber = wxString::Format( "%d", aModelPinIndex + 1 );
1135
1136 if( !pinName.IsEmpty() && pinName != pinNumber )
1137 pinNumber += wxString::Format( wxT( " (%s)" ), pinName );
1138
1139 return pinNumber;
1140}
1141
1142
1143template <typename T>
1144int DIALOG_SIM_MODEL<T>::getModelPinIndex( const wxString& aModelPinString ) const
1145{
1146 if( aModelPinString == "Not Connected" )
1148
1149 int length = aModelPinString.Find( " " );
1150
1151 if( length == wxNOT_FOUND )
1152 length = static_cast<int>( aModelPinString.Length() );
1153
1154 long result = 0;
1155 aModelPinString.Mid( 0, length ).ToCLong( &result );
1156
1157 return static_cast<int>( result - 1 );
1158}
1159
1160
1161template <typename T>
1162void DIALOG_SIM_MODEL<T>::onRadioButton( wxCommandEvent& aEvent )
1163{
1164 m_prevModel = nullptr; // Ensure the Model panel will be rebuild after updating other params.
1165 updateWidgets();
1166}
1167
1168
1169template <typename T>
1170void DIALOG_SIM_MODEL<T>::onLibrarayPathText( wxCommandEvent& aEvent )
1171{
1172 m_rbLibraryModel->SetValue( true );
1173 updateWidgets();
1174}
1175
1176
1177template <typename T>
1179{
1180 if( m_rbLibraryModel->GetValue() )
1181 {
1182 wxString path = m_libraryPathText->GetValue();
1183
1184 if( !path.IsEmpty() )
1185 {
1186 try
1187 {
1188 loadLibrary( path );
1189 updateWidgets();
1190 }
1191 catch( const IO_ERROR& )
1192 {
1193 // TODO: Add an infobar to report the error?
1194 }
1195 }
1196 }
1197}
1198
1199
1200template <typename T>
1202{
1203 if( !m_inKillFocus )
1204 {
1205 m_inKillFocus = true;
1206
1207 wxCommandEvent dummy;
1208 onLibraryPathTextEnter( dummy );
1209
1210 m_inKillFocus = false;
1211 }
1212}
1213
1214
1215template <typename T>
1216void DIALOG_SIM_MODEL<T>::onBrowseButtonClick( wxCommandEvent& aEvent )
1217{
1218 static wxString s_mruPath;
1219
1220 wxString path = s_mruPath.IsEmpty() ? Prj().GetProjectPath() : s_mruPath;
1221 wxFileDialog dlg( this, _( "Browse Models" ), path );
1222
1223 if( dlg.ShowModal() == wxID_CANCEL )
1224 return;
1225
1226 m_rbLibraryModel->SetValue( true );
1227
1228 path = dlg.GetPath();
1229 wxFileName fn( path );
1230
1231 s_mruPath = fn.GetPath();
1232
1233 if( fn.MakeRelativeTo( Prj().GetProjectPath() ) && !fn.GetFullPath().StartsWith( wxS( ".." ) ) )
1234 path = fn.GetFullPath();
1235
1236 loadLibrary( path, true );
1237 updateWidgets();
1238}
1239
1240
1241template <typename T>
1242void DIALOG_SIM_MODEL<T>::onModelNameChoice( wxCommandEvent& aEvent )
1243{
1244 if( isIbisLoaded() )
1245 {
1246 wxArrayString pinLabels;
1247 SIM_MODEL_KIBIS* modelkibis = dynamic_cast<SIM_MODEL_KIBIS*>( &curModel() );
1248
1249 wxCHECK2( modelkibis, return );
1250
1251 for( std::pair<wxString, wxString> strs : modelkibis->GetIbisPins() )
1252 pinLabels.Add( strs.first + wxT( " - " ) + strs.second );
1253
1254 m_pinCombobox->Set( pinLabels );
1255
1256 wxArrayString emptyArray;
1257 m_pinModelCombobox->Set( emptyArray );
1258 }
1259
1260 m_rbLibraryModel->SetValue( true );
1261 updateWidgets();
1262}
1263
1264
1265template <typename T>
1266void DIALOG_SIM_MODEL<T>::onPinCombobox( wxCommandEvent& aEvent )
1267{
1268 wxArrayString modelLabels;
1269
1270 SIM_MODEL_KIBIS& ibisModel = static_cast<SIM_MODEL_KIBIS&>( curModel() );
1271
1272 std::vector<std::pair<std::string, std::string>> strs = ibisModel.GetIbisPins();
1273 std::string pinNumber = strs.at( m_pinCombobox->GetSelection() ).first;
1274
1275 const SIM_LIBRARY_KIBIS* ibisLibrary = dynamic_cast<const SIM_LIBRARY_KIBIS*>( library() );
1276
1277 ibisModel.ChangePin( *ibisLibrary, pinNumber );
1278
1279 ibisModel.m_enableDiff = ibisLibrary->isPinDiff( ibisModel.GetComponentName(), pinNumber );
1280
1281 for( wxString modelName : ibisModel.GetIbisModels() )
1282 modelLabels.Add( modelName );
1283
1284 m_pinModelCombobox->Set( modelLabels );
1285
1286 if( m_pinModelCombobox->GetCount() == 1 )
1287 m_pinModelCombobox->SetSelection( 0 );
1288 else
1289 m_pinModelCombobox->SetSelection( -1 );
1290
1291 updateWidgets();
1292}
1293
1294
1295template <typename T>
1297{
1298 m_pinCombobox->SetSelection( m_pinCombobox->FindString( m_pinCombobox->GetValue() ) );
1299
1300 onPinModelCombobox( aEvent );
1301}
1302
1303
1304template <typename T>
1305void DIALOG_SIM_MODEL<T>::onPinModelCombobox( wxCommandEvent& aEvent )
1306{
1307 updateWidgets();
1308}
1309
1310
1311template <typename T>
1313{
1314 m_pinModelCombobox->SetSelection( m_pinModelCombobox->FindString( m_pinModelCombobox->GetValue() ) );
1315}
1316
1317template <typename T>
1319{
1320 SIM_MODEL_KIBIS* modelkibis = dynamic_cast<SIM_MODEL_KIBIS*>( &curModel() );
1321
1322 wxCHECK( modelkibis, /* void */ );
1323
1324 bool diff = m_differentialCheckbox->GetValue() && modelkibis->CanDifferential();
1325 modelkibis->SwitchSingleEndedDiff( diff );
1326
1327 updateWidgets();
1328}
1329
1330
1331template <typename T>
1332void DIALOG_SIM_MODEL<T>::onDeviceTypeChoice( wxCommandEvent& aEvent )
1333{
1334 m_rbBuiltinModel->SetValue( true );
1335
1336 for( SIM_MODEL::DEVICE_T deviceType : SIM_MODEL::DEVICE_T_ITERATOR() )
1337 {
1338 if( SIM_MODEL::DeviceInfo( deviceType ).description == m_deviceChoice->GetStringSelection() )
1339 {
1340 m_curModelType = m_curModelTypeOfDeviceType.at( deviceType );
1341 break;
1342 }
1343 }
1344
1345 updateWidgets();
1346}
1347
1348
1349template <typename T>
1350void DIALOG_SIM_MODEL<T>::onWaveformChoice( wxCommandEvent& aEvent )
1351{
1352 SIM_MODEL::DEVICE_T deviceType = curModel().GetDeviceType();
1353 wxString typeDescription = m_waveformChoice->GetStringSelection();
1354
1355 for( SIM_MODEL::TYPE type : { SIM_MODEL::TYPE::KIBIS_DEVICE,
1356 SIM_MODEL::TYPE::KIBIS_DRIVER_DC,
1357 SIM_MODEL::TYPE::KIBIS_DRIVER_RECT,
1358 SIM_MODEL::TYPE::KIBIS_DRIVER_PRBS } )
1359 {
1360 if( equivalent( deviceType, SIM_MODEL::TypeInfo( type ).deviceType )
1361 && typeDescription == SIM_MODEL::TypeInfo( type ).description )
1362 {
1363 int idx = m_modelNameChoice->GetSelection();
1364
1365 auto& baseModel = static_cast<SIM_MODEL_KIBIS&>( m_libraryModelsMgr.GetModels()[idx].get() );
1366
1367 m_libraryModelsMgr.SetModel( idx, std::make_unique<SIM_MODEL_KIBIS>( type, baseModel ) );
1368
1369 try
1370 {
1371 m_libraryModelsMgr.GetModels()[idx].get().ReadDataFields( &m_fields, m_sortedPartPins );
1372 }
1373 catch( IO_ERROR& err )
1374 {
1375 DisplayErrorMessage( this, err.What() );
1376 }
1377
1378 m_curModelType = type;
1379 break;
1380 }
1381 }
1382
1383 m_curModelTypeOfDeviceType.at( deviceType ) = m_curModelType;
1384 updateWidgets();
1385}
1386
1387
1388template <typename T>
1389void DIALOG_SIM_MODEL<T>::onTypeChoice( wxCommandEvent& aEvent )
1390{
1391 SIM_MODEL::DEVICE_T deviceType = curModel().GetDeviceType();
1392 wxString typeDescription = m_deviceSubtypeChoice->GetStringSelection();
1393
1394 for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() )
1395 {
1396 if( equivalent( deviceType, SIM_MODEL::TypeInfo( type ).deviceType )
1397 && typeDescription == SIM_MODEL::TypeInfo( type ).description )
1398 {
1399 m_curModelType = type;
1400 break;
1401 }
1402 }
1403
1404 m_curModelTypeOfDeviceType.at( deviceType ) = m_curModelType;
1405 updateWidgets();
1406}
1407
1408
1409template <typename T>
1410void DIALOG_SIM_MODEL<T>::onPageChanging( wxBookCtrlEvent& event )
1411{
1412 updateModelCodeTab( &curModel() );
1413}
1414
1415
1416template <typename T>
1418{
1419 int symbolPinIndex = aEvent.GetRow();
1420 wxString oldModelPinName = aEvent.GetString();
1421 wxString modelPinName = m_pinAssignmentsGrid->GetCellValue( aEvent.GetRow(), aEvent.GetCol() );
1422
1423 int oldModelPinIndex = getModelPinIndex( oldModelPinName );
1424 int modelPinIndex = getModelPinIndex( modelPinName );
1425
1426 if( oldModelPinIndex != SIM_MODEL::PIN::NOT_CONNECTED )
1427 curModel().SetPinSymbolPinNumber( oldModelPinIndex, "" );
1428
1429 if( modelPinIndex != SIM_MODEL::PIN::NOT_CONNECTED )
1430 {
1431 curModel().SetPinSymbolPinNumber( modelPinIndex,
1432 std::string( m_sortedPartPins.at( symbolPinIndex )->GetShownNumber().ToUTF8() ) );
1433 }
1434
1435 updatePinAssignments( &curModel(), FORCE_UPDATE_PINS );
1436
1437 aEvent.Skip();
1438}
1439
1440
1441template <typename T>
1443{
1444 wxGridUpdateLocker deferRepaintsTillLeavingScope( m_pinAssignmentsGrid );
1445
1446 int gridWidth = KIPLATFORM::UI::GetUnobscuredSize( m_pinAssignmentsGrid ).x;
1447 m_pinAssignmentsGrid->SetColSize( PIN_COLUMN::MODEL, gridWidth / 2 );
1448 m_pinAssignmentsGrid->SetColSize( PIN_COLUMN::SYMBOL, gridWidth / 2 );
1449
1450 aEvent.Skip();
1451}
1452
1453
1454template <typename T>
1456{
1457 // By default, when a property grid is focused, the textbox is not immediately focused until
1458 // Tab key is pressed. This is inconvenient, so we fix that here.
1459
1460 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1461 wxPGProperty* selected = grid->GetSelection();
1462
1463 if( !selected )
1464 selected = grid->wxPropertyGridInterface::GetFirst();
1465
1466#if wxCHECK_VERSION( 3, 3, 0 )
1467 if( selected )
1468 grid->DoSelectProperty( selected, wxPGSelectPropertyFlags::Focus );
1469#else
1470 if( selected )
1471 grid->DoSelectProperty( selected, wxPG_SEL_FOCUS );
1472#endif
1473
1474 aEvent.Skip();
1475}
1476
1477
1478template <typename T>
1479void DIALOG_SIM_MODEL<T>::onParamGridSelectionChange( wxPropertyGridEvent& aEvent )
1480{
1481 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1482
1483 // Jump over categories.
1484 if( grid->GetSelection() && grid->GetSelection()->IsCategory() )
1485 {
1486 wxPGProperty* selection = grid->GetSelection();
1487
1488 // If the new selection is immediately above the previous selection, we jump up. Otherwise
1489 // we jump down. We do this by simulating up or down arrow keys.
1490
1491 wxPropertyGridIterator it = grid->GetIterator( wxPG_ITERATE_VISIBLE, selection );
1492 it.Next();
1493
1494 wxKeyEvent* keyEvent = new wxKeyEvent( wxEVT_KEY_DOWN );
1495
1496 if( *it == m_prevParamGridSelection )
1497 {
1498 if( !selection->IsExpanded() )
1499 {
1500 grid->Expand( selection );
1501 keyEvent->m_keyCode = WXK_DOWN;
1502 wxQueueEvent( grid, keyEvent );
1503
1504 // Does not work for some reason.
1505 /*m_paramGrid->DoSelectProperty( selection->Item( selection->GetChildCount() - 1 ),
1506 wxPG_SEL_FOCUS );*/
1507 }
1508 else
1509 {
1510 keyEvent->m_keyCode = WXK_UP;
1511 wxQueueEvent( grid, keyEvent );
1512 }
1513 }
1514 else
1515 {
1516 if( !selection->IsExpanded() )
1517 grid->Expand( selection );
1518
1519 keyEvent->m_keyCode = WXK_DOWN;
1520 wxQueueEvent( grid, keyEvent );
1521 }
1522
1523 m_prevParamGridSelection = grid->GetSelection();
1524 return;
1525 }
1526
1527 wxWindow* editorControl = grid->GetEditorControl();
1528
1529 if( !editorControl )
1530 {
1531 m_prevParamGridSelection = grid->GetSelection();
1532 return;
1533 }
1534
1535 // Without this the user had to press tab before they could edit the field.
1536 editorControl->SetFocus();
1537 m_prevParamGridSelection = grid->GetSelection();
1538}
1539
1540
1541template <typename T>
1542void DIALOG_SIM_MODEL<T>::onUpdateUI( wxUpdateUIEvent& aEvent )
1543{
1544 // This is currently patched in wxPropertyGrid::ScrollWindow() in the Mac wxWidgets fork.
1545 // However, we may need this version if it turns out to be an issue on other platforms and
1546 // we can't get it upstreamed.
1547#if 0
1548 // It's a shame to do this on the UpdateUI event, but neither the wxPropertyGridManager,
1549 // wxPropertyGridPage, wxPropertyGrid, nor the wxPropertyGrid's GetCanvas() window appear
1550 // to get scroll events.
1551
1552 wxPropertyGrid* grid = m_paramGrid->GetGrid();
1553 wxTextCtrl* ctrl = grid->GetEditorTextCtrl();
1554
1555 if( ctrl )
1556 {
1557 wxRect ctrlRect = ctrl->GetScreenRect();
1558 wxRect gridRect = grid->GetScreenRect();
1559
1560 if( ctrlRect.GetTop() < gridRect.GetTop() || ctrlRect.GetBottom() > gridRect.GetBottom() )
1561 grid->ClearSelection();
1562 }
1563#endif
1564}
1565
1566
1567template <typename T>
1568void DIALOG_SIM_MODEL<T>::adjustParamGridColumns( int aWidth, bool aForce )
1569{
1570 wxPropertyGrid* grid = m_paramGridMgr->GetGrid();
1571 int margin = 15;
1572 int indent = 20;
1573
1574 if( aWidth != m_lastParamGridWidth || aForce )
1575 {
1576 m_lastParamGridWidth = aWidth;
1577
1578 grid->FitColumns();
1579
1580 std::vector<int> colWidths;
1581
1582 for( size_t ii = 0; ii < grid->GetColumnCount(); ii++ )
1583 {
1584 if( ii == PARAM_COLUMN::DESCRIPTION )
1585 colWidths.push_back( grid->GetState()->GetColumnWidth( ii ) + margin + indent );
1586 else if( ii == PARAM_COLUMN::VALUE )
1587 colWidths.push_back( std::max( 72, grid->GetState()->GetColumnWidth( ii ) ) + margin );
1588 else
1589 colWidths.push_back( 60 + margin );
1590
1591 aWidth -= colWidths[ ii ];
1592 }
1593
1594 for( size_t ii = 0; ii < grid->GetColumnCount(); ii++ )
1595 grid->SetColumnProportion( ii, colWidths[ ii ] );
1596
1597 grid->ResetColumnSizes();
1598 grid->RefreshEditor();
1599 }
1600}
1601
1602
1603template <typename T>
1605{
1606 adjustParamGridColumns( event.GetSize().GetX(), false );
1607
1608 event.Skip();
1609}
1610
1611
1612
1613template class DIALOG_SIM_MODEL<SCH_SYMBOL>;
1614template class DIALOG_SIM_MODEL<LIB_SYMBOL>;
const char * name
Definition: DXF_plotter.cpp:57
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap)
Definition: bitmap.cpp:110
@ INVALID_BITMAP
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
Class DIALOG_SIM_MODEL_BASE.
wxPropertyGridManager * m_paramGridMgr
STD_BITMAP_BUTTON * m_browseButton
wxStyledTextCtrl * m_subckt
wxPropertyGridPage * m_paramGrid
wxStyledTextCtrl * m_codePreview
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
int findSymbolPinRow(const wxString &aSymbolPinNumber) const
void onModelNameChoice(wxCommandEvent &aEvent) override
void onRadioButton(wxCommandEvent &aEvent) override
SCINTILLA_TRICKS * m_scintillaTricksSubckt
int getModelPinIndex(const wxString &aModelPinString) const
void onDifferentialCheckbox(wxCommandEvent &event) override
void onParamGridSelectionChange(wxPropertyGridEvent &aEvent)
void removeOrphanedPinAssignments(SIM_MODEL *aModel)
void adjustParamGridColumns(int aWidth, bool aForce)
wxPGProperty * newParamProperty(SIM_MODEL *aModel, int aParamIndex) const
void onPinModelCombobox(wxCommandEvent &event) override
const SIM_LIBRARY * library() const
SIM_MODEL & curModel() const
void onBrowseButtonClick(wxCommandEvent &aEvent) override
void onPinComboboxTextEnter(wxCommandEvent &event) override
void onPinCombobox(wxCommandEvent &event) override
SCINTILLA_TRICKS * m_scintillaTricksCode
std::vector< LIB_PIN * > m_sortedPartPins
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
bool loadLibrary(const wxString &aLibraryPath, bool aForceReload=false)
void updateIbisWidgets(SIM_MODEL *aModel)
void onParamGridSetFocus(wxFocusEvent &aEvent)
void updateModelParamsTab(SIM_MODEL *aModel)
bool TransferDataFromWindow() override
void updateModelCodeTab(SIM_MODEL *aModel)
void onSizeParamGrid(wxSizeEvent &event) override
void onPageChanging(wxNotebookEvent &event) override
void onWaveformChoice(wxCommandEvent &aEvent) override
void onPinModelComboboxTextEnter(wxCommandEvent &event) override
void onLibraryPathTextEnter(wxCommandEvent &aEvent) override
void onDeviceTypeChoice(wxCommandEvent &aEvent) override
bool TransferDataToWindow() override
void addParamPropertyIfRelevant(SIM_MODEL *aModel, int aParamIndex)
void onLibrarayPathText(wxCommandEvent &aEvent) override
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:61
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
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:223
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:135
Schematic editor (Eeschema) main window.
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:51
Add cut/copy/paste, dark theme, autocomplete and brace highlighting to a wxStyleTextCtrl instance.
bool isPinDiff(const std::string &aComp, const std::string &aPinNumber) const
static constexpr auto DIFF_FIELD
static constexpr auto MODEL_FIELD
static constexpr auto PIN_FIELD
static constexpr auto LIBRARY_FIELD
Definition: sim_library.h:35
static constexpr auto NAME_FIELD
Definition: sim_library.h:36
bool ChangePin(const SIM_LIBRARY_KIBIS &aLib, std::string aPinNumber)
update the list of available models based on the pin number.
std::vector< std::string > GetIbisModels() const
std::string GetComponentName() const
bool CanDifferential() const
void SwitchSingleEndedDiff(bool aDiff) override
std::vector< std::pair< std::string, std::string > > GetIbisPins() const
std::string GetSpiceCode() const
void ReadDataFields(const std::vector< SCH_FIELD > *aFields, const std::vector< LIB_PIN * > &aPins)
Definition: sim_model.cpp:423
static TYPE ReadTypeFromFields(const std::vector< SCH_FIELD > &aFields, REPORTER &aReporter)
Definition: sim_model.cpp:386
static INFO TypeInfo(TYPE aType)
Definition: sim_model.cpp:101
static void SetFieldValue(std::vector< SCH_FIELD > &aFields, const wxString &aFieldName, const std::string &aValue)
Definition: sim_model.cpp:664
int GetPinCount() const
Definition: sim_model.h:472
const SPICE_GENERATOR & SpiceGenerator() const
Definition: sim_model.h:436
virtual const PARAM & GetParam(unsigned aParamIndex) const
Definition: sim_model.cpp:779
static bool InferSimModel(T &aSymbol, std::vector< SCH_FIELD > *aFields, bool aResolve, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
Definition: sim_model.cpp:1063
static std::string GetFieldValue(const std::vector< SCH_FIELD > *aFields, const wxString &aFieldName, bool aResolve=true)
Definition: sim_model.cpp:645
int GetParamCount() const
Definition: sim_model.h:482
void SetPinSymbolPinNumber(int aPinIndex, const std::string &aSymbolPinNumber)
Definition: sim_model.cpp:749
DEVICE_INFO GetDeviceInfo() const
Definition: sim_model.h:461
DEVICE_T GetDeviceType() const
Definition: sim_model.h:464
static DEVICE_INFO DeviceInfo(DEVICE_T aDeviceType)
Definition: sim_model.cpp:56
const PIN & GetPin(unsigned aIndex) const
Definition: sim_model.h:473
virtual bool HasAutofill() const
Definition: sim_model.h:501
void SetParamValue(int aParamIndex, const std::string &aValue, SIM_VALUE::NOTATION aNotation=SIM_VALUE::NOTATION::SI)
Definition: sim_model.cpp:848
void SetIsStoredInValue(bool aIsStoredInValue)
Definition: sim_model.h:507
virtual bool HasPrimaryValue() const
Definition: sim_model.h:502
TYPE GetType() const
Definition: sim_model.h:465
const SIM_MODEL::PARAM & GetParam() const
Definition: sim_property.h:60
@ TYPE_BOOL
Definition: sim_value.h:67
@ TYPE_FLOAT_VECTOR
Definition: sim_value.h:75
@ TYPE_BOOL_VECTOR
Definition: sim_value.h:73
@ TYPE_INT
Definition: sim_value.h:68
@ TYPE_FLOAT
Definition: sim_value.h:69
@ TYPE_INT_VECTOR
Definition: sim_value.h:74
@ TYPE_COMPLEX_VECTOR
Definition: sim_value.h:76
@ TYPE_STRING
Definition: sim_value.h:71
@ TYPE_COMPLEX
Definition: sim_value.h:70
Special netlist exporter flavor that allows one to override simulation commands.
virtual std::string Preview(const SPICE_ITEM &aItem) const
void SetBitmap(const wxBitmapBundle &aBmp)
void ClearRows()
wxWidgets recently added an ASSERT which fires if the position is greater than or equal to the number...
Definition: wx_grid.h:157
A wrapper for reporting to a wxString object.
Definition: reporter.h:164
bool HasMessage() const override
Returns true if the reporter client is non-empty.
Definition: reporter.cpp:71
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:305
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_UPDATE_PINS
#define _(s)
@ NONE
Definition: kibis.h:54
PROJECT & Prj()
Definition: kicad.cpp:595
wxSize GetUnobscuredSize(const wxWindow *aWindow)
Tries to determine the size of the viewport of a scrollable widget (wxDataViewCtrl,...
Definition: gtk/ui.cpp:195
KICOMMON_API wxFont GetInfoFont(wxWindow *aWindow)
Definition: ui_common.cpp:151
BITMAPS PinShapeGetBitmap(GRAPHIC_PINSHAPE aShape)
Definition: pin_type.cpp:243
SIM_MODEL::TYPE TYPE
Definition: sim_model.cpp:53
#define SIM_PINS_FIELD
Definition: sim_model.h:54
#define SIM_DEVICE_FIELD
Definition: sim_model.h:52
#define SIM_REFERENCE_FIELD
Definition: sim_model.h:49
#define SIM_PARAMS_FIELD
Definition: sim_model.h:55
#define SIM_DEVICE_SUBTYPE_FIELD
Definition: sim_model.h:53
std::vector< FAB_LAYER_COLOR > dummy
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
std::vector< std::string > enumValues
Definition: sim_model.h:389
SIM_VALUE::TYPE type
Definition: sim_model.h:380
std::string defaultValue
Definition: sim_model.h:383
std::string description
Definition: sim_model.h:384
std::string value
Definition: sim_model.h:401
const INFO & info
Definition: sim_model.h:402
std::string symbolPinNumber
Definition: sim_model.h:309
const std::string name
Definition: sim_model.h:308
static constexpr auto NOT_CONNECTED
Definition: sim_model.h:311
std::string modelName
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".