diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt index 3bd5ef0f13..5cef63d2c8 100644 --- a/eeschema/CMakeLists.txt +++ b/eeschema/CMakeLists.txt @@ -285,6 +285,7 @@ set( EESCHEMA_SRCS sim/sim_library_spice.cpp sim/sim_library_kibis.cpp sim/sim_lib_mgr.cpp + sim/sim_serde.cpp sim/sim_model.cpp sim/sim_model_behavioral.cpp sim/sim_model_ideal.cpp diff --git a/eeschema/sim/sim_model.cpp b/eeschema/sim/sim_model.cpp index a8083e0039..c525521fa6 100644 --- a/eeschema/sim/sim_model.cpp +++ b/eeschema/sim/sim_model.cpp @@ -54,27 +54,6 @@ using DEVICE_TYPE = SIM_MODEL::DEVICE_TYPE_; using TYPE = SIM_MODEL::TYPE; -namespace SIM_MODEL_PARSER -{ - using namespace SIM_MODEL_GRAMMAR; - - template struct fieldParamValuePairsSelector : std::false_type {}; - template <> struct fieldParamValuePairsSelector : std::true_type {}; - template <> struct fieldParamValuePairsSelector : std::true_type {}; - template <> struct fieldParamValuePairsSelector : std::true_type {}; - - - template struct pinSequenceSelector : std::false_type {}; - template <> struct pinSequenceSelector : std::true_type {}; - - template struct fieldInferValueSelector : std::false_type {}; - template <> struct fieldInferValueSelector : std::true_type {}; - template <> struct fieldInferValueSelector : std::true_type {}; - template <> struct fieldInferValueSelector> : std::true_type {}; - template <> struct fieldInferValueSelector : std::true_type {}; -} - - SIM_MODEL::DEVICE_INFO SIM_MODEL::DeviceTypeInfo( DEVICE_TYPE_ aDeviceType ) { switch( aDeviceType ) @@ -425,9 +404,9 @@ TYPE SIM_MODEL::ReadTypeFromFields( const std::vector& aFields, int aSymbolPi // Still no type information. // We try to infer the model from the mandatory fields in this case. - return InferTypeFromRefAndValue( GetFieldValue( &aFields, REFERENCE_FIELD ), - GetFieldValue( &aFields, VALUE_FIELD ), - aSymbolPinCount ); + return SIM_SERDE::InferTypeFromRefAndValue( GetFieldValue( &aFields, REFERENCE_FIELD ), + GetFieldValue( &aFields, VALUE_FIELD ), + aSymbolPinCount ); } @@ -451,45 +430,6 @@ DEVICE_TYPE SIM_MODEL::InferDeviceTypeFromRef( const std::string& aRef ) } -TYPE SIM_MODEL::InferTypeFromRefAndValue( const std::string& aRef, const std::string& aValue, - int aSymbolPinCount ) -{ - std::string typeString; - - try - { - tao::pegtl::string_input<> in( aValue, VALUE_FIELD ); - auto root = tao::pegtl::parse_tree::parse( in ); - - for( const auto& node : root->children ) - { - if( node->is_type() ) - typeString = node->string(); - } - } - catch( const tao::pegtl::parse_error& ) - { - } - - DEVICE_TYPE deviceType = InferDeviceTypeFromRef( aRef ); - - // Exception. Potentiometer model is determined from pin count. - if( deviceType == DEVICE_TYPE_::R && aSymbolPinCount == 3 ) - return TYPE::R_POT; - - for( TYPE type : TYPE_ITERATOR() ) - { - if( TypeInfo( type ).deviceType == deviceType && TypeInfo( type ).fieldValue == typeString ) - return type; - } - - return TYPE::NONE; -} - - template TYPE SIM_MODEL::InferTypeFromLegacyFields( const std::vector& aFields ) { @@ -967,17 +907,30 @@ std::unique_ptr SIM_MODEL::Create( TYPE aType ) SIM_MODEL::SIM_MODEL( TYPE aType ) : - SIM_MODEL( aType, std::make_unique( *this ) ) + SIM_MODEL( aType, + std::make_unique( *this ), + std::make_unique( *this ) ) +{ +} + +SIM_MODEL::SIM_MODEL( TYPE aType, + std::unique_ptr aSpiceGenerator ) : + SIM_MODEL( aType, + std::move( aSpiceGenerator ), + std::make_unique( *this ) ) { } -SIM_MODEL::SIM_MODEL( TYPE aType, std::unique_ptr aSpiceGenerator ) : - m_baseModel( nullptr ), - m_spiceGenerator( std::move( aSpiceGenerator ) ), - m_type( aType ), - m_isEnabled( true ), - m_isInferred( false ) +SIM_MODEL::SIM_MODEL( TYPE aType, + std::unique_ptr aSpiceGenerator, + std::unique_ptr aSerde ) : + m_baseModel( nullptr ), + m_serde( std::move( aSerde ) ), + m_spiceGenerator( std::move( aSpiceGenerator ) ), + m_type( aType ), + m_isEnabled( true ), + m_isInferred( false ) { } @@ -1037,127 +990,21 @@ void SIM_MODEL::WriteInferredDataFields( std::vector& aFields, const std::str } -std::string SIM_MODEL::GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const -{ - std::string result; - - if( aIsFirst ) - aIsFirst = false; - else - result.append( " " ); - - std::string name = aParam.info.name; - - // Because of collisions with instance parameters, we append some model parameters with "_". - if( boost::ends_with( aParam.info.name, "_" ) ) - name = aParam.info.name.substr( 0, aParam.info.name.length() - 1); - - std::string value = aParam.value->ToString(); - if( value.find( " " ) != std::string::npos ) - value = "\"" + value + "\""; - - result.append( fmt::format( "{}={}", aParam.info.name, value ) ); - return result; -} - - std::string SIM_MODEL::GenerateValueField( const std::string& aPairSeparator ) const { - std::string result; - bool isFirst = true; - - for( int i = 0; i < GetParamCount(); ++i ) - { - const PARAM& param = GetParam( i ); - - if( i == 0 && hasPrimaryValue() ) - { - result.append( param.value->ToString() ); - isFirst = false; - continue; - } - - if( param.value->ToString() == "" ) - continue; - - result.append( GenerateParamValuePair( param, isFirst ) ); - } - - if( result == "" ) - result = GetDeviceTypeInfo().fieldValue; - - return result; + return m_serde->GenerateValue(); } std::string SIM_MODEL::GenerateParamsField( const std::string& aPairSeparator ) const { - std::string result; - bool isFirst = true; - - for( const PARAM& param : m_params ) - { - if( param.value->ToString() == "" ) - continue; - - result.append( GenerateParamValuePair( param, isFirst ) ); - } - - return result; + return m_serde->GenerateParams(); } void SIM_MODEL::ParseParamsField( const std::string& aParamsField ) { - tao::pegtl::string_input<> in( aParamsField, "Sim_Params" ); - std::unique_ptr root; - - try - { - // Using parse tree instead of actions because we don't care about performance that much, - // and having a tree greatly simplifies things. - root = tao::pegtl::parse_tree::parse< - SIM_MODEL_PARSER::fieldParamValuePairsGrammar, - SIM_MODEL_PARSER::fieldParamValuePairsSelector, - tao::pegtl::nothing, - SIM_MODEL_PARSER::control> - ( in ); - } - catch( const tao::pegtl::parse_error& e ) - { - THROW_IO_ERROR( e.what() ); - } - - std::string paramName; - - for( const auto& node : root->children ) - { - if( node->is_type() ) - paramName = node->string(); - // TODO: Do something with number. - // It doesn't seem too useful? - else if( node->is_type() - || node->is_type() ) - { - wxASSERT( paramName != "" ); - // TODO: Shouldn't be named "...fromSpiceCode" here... - - SetParamValue( paramName, node->string(), SIM_VALUE_GRAMMAR::NOTATION::SI ); - } - else if( node->is_type() ) - { - std::string str = node->string(); - - // Unescape quotes. - boost::replace_all( str, "\\\"", "\"" ); - - SetParamValue( paramName, str, SIM_VALUE_GRAMMAR::NOTATION::SI ); - } - else - { - wxFAIL; - } - } + m_serde->ParseParams( aParamsField ); } @@ -1165,51 +1012,13 @@ void SIM_MODEL::ParsePinsField( unsigned aSymbolPinCount, const std::string& aPi { CreatePins( aSymbolPinCount ); - if( aPinsField == "" ) - return; - - tao::pegtl::string_input<> in( aPinsField, PINS_FIELD ); - std::unique_ptr root; - - try - { - root = tao::pegtl::parse_tree::parse( in ); - } - catch( const tao::pegtl::parse_error& e ) - { - THROW_IO_ERROR( e.what() ); - } - - if( static_cast( root->children.size() ) != GetPinCount() ) - { - THROW_IO_ERROR( wxString::Format( _( "%s describes %lu pins, expected %u" ), - PINS_FIELD, - root->children.size(), - GetPinCount() ) ); - } - - for( int pinIndex = 0; pinIndex < static_cast( root->children.size() ); ++pinIndex ) - { - if( root->children.at( pinIndex )->string() == "~" ) - SetPinSymbolPinNumber( pinIndex, "" ); - else - SetPinSymbolPinNumber( pinIndex, root->children.at( pinIndex )->string() ); - } + m_serde->ParsePins( aPinsField ); } void SIM_MODEL::ParseEnableField( const std::string& aEnableField ) { - if( aEnableField == "" ) - return; - - char c = boost::to_lower_copy( aEnableField )[0]; - - if( c == 'n' || c == 'f' || c == '0' ) - m_isEnabled = false; + m_serde->ParseEnable( aEnableField ); } @@ -1234,54 +1043,15 @@ void SIM_MODEL::InferredReadDataFields( unsigned aSymbolPinCount, const std::vec return; // TODO: Don't call this multiple times. - if( InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ), - GetFieldValue( aFields, VALUE_FIELD ), - aSymbolPinCount ) != GetType() ) + if( SIM_SERDE::InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ), + GetFieldValue( aFields, VALUE_FIELD ), + aSymbolPinCount ) != GetType() ) { // Not an inferred model. Nothing to do here. return; } - try - { - // TODO: Don't call this multiple times. - tao::pegtl::string_input<> in( GetFieldValue( aFields, VALUE_FIELD ), VALUE_FIELD ); - auto root = tao::pegtl::parse_tree::parse( in ); - - for( const auto& node : root->children ) - { - if( node->is_type() ) - { - if( hasPrimaryValue() ) - { - for( const auto& subnode : node->children ) - { - if( subnode->is_type>() ) - { - SetParamValue( 0, subnode->string() ); - } - } - } - else - { - THROW_IO_ERROR( - wxString::Format( _( "Simulation model of type '%s' cannot have a primary value (which is '%s') in Value field" ), - GetTypeInfo().fieldValue, - node->string() ) ); - } - } - else if( node->is_type() ) - ParseParamsField( node->string() ); - } - } - catch( const tao::pegtl::parse_error& e ) - { - THROW_IO_ERROR( e.what() ); - } + m_serde->ParseValue( GetFieldValue( aFields, VALUE_FIELD ) ); SetIsInferred( true ); } diff --git a/eeschema/sim/sim_model.h b/eeschema/sim/sim_model.h index cda2396484..2a874be434 100644 --- a/eeschema/sim/sim_model.h +++ b/eeschema/sim/sim_model.h @@ -39,102 +39,7 @@ class SIM_LIBRARY; class SPICE_GENERATOR; - - -namespace SIM_MODEL_GRAMMAR -{ - using namespace SIM_VALUE_GRAMMAR; - - struct sep : plus {}; - - - struct legacyPinNumber : digits {}; - struct legacyPinSequence : list {}; - - struct legacyPinSequenceGrammar : must {}; - - - struct pinNumber : plus, any> {}; - struct pinSequence : list {}; - struct pinSequenceGrammar : must, - opt, - opt, - tao::pegtl::eof> {}; - - struct param : plus {}; - - struct unquotedString : plus, any> {}; - struct quotedStringContent : star>, any> {}; // TODO: Allow escaping '"'. - struct quotedString : seq, - quotedStringContent, - one<'"'>> {}; - - struct fieldParamValuePair : if_must, - one<'='>, - opt, - sor> {}; - struct fieldParamValuePairs : list {}; - struct fieldParamValuePairsGrammar : must, - opt, - opt, - tao::pegtl::eof> {}; - - struct fieldInferValueType : plus {}; - struct fieldInferValuePrimaryValue : seq>, - tao::pegtl::digit>>, - // END HACK. - number, - // Hackish: match anything until param-value pairs. - // Because the user may want to write something like - // "10k 30% 30mW w=0.4", but we care only about the - // "10k" and "w=0.4". - star>, - any>> {}; - struct fieldInferValue : sor>, - seq, - opt, - opt>> {}; - struct fieldInferValueGrammar : must, - fieldInferValue, - opt, - tao::pegtl::eof> {}; - - - template inline constexpr const char* errorMessage = nullptr; - template <> inline constexpr auto errorMessage> = ""; - template <> inline constexpr auto errorMessage> = ""; - template <> inline constexpr auto errorMessage>> = ""; - template <> inline constexpr auto errorMessage> = - "expected '='"; - template <> inline constexpr auto errorMessage> = - "expected quoted or unquoted string"; - template <> inline constexpr auto errorMessage = - "expected parameter=value pairs"; - template <> inline constexpr auto errorMessage> = ""; - template <> inline constexpr auto errorMessage = - "expected parameter=value pairs, together possibly preceded by a type or primary value"; - template <> inline constexpr auto errorMessage = - "expected end of string"; - - struct error - { - template static constexpr bool raise_on_failure = false; - template static constexpr auto message = errorMessage; - }; - - template using control = must_if::control; -} +class SIM_SERDE; class SIM_MODEL @@ -485,9 +390,6 @@ public: static DEVICE_TYPE_ InferDeviceTypeFromRef( const std::string& aRef ); - static TYPE InferTypeFromRefAndValue( const std::string& aRef, const std::string& aValue, - int aSymbolPinCount ); - template static TYPE InferTypeFromLegacyFields( const std::vector& aFields ); @@ -606,6 +508,8 @@ public: // Can modifying a model parameter also modify other parameters? virtual bool HasAutofill() const { return false; } + virtual bool HasPrimaryValue() const { return false; } + void SetIsEnabled( bool aIsEnabled ) { m_isEnabled = aIsEnabled; } bool IsEnabled() const { return m_isEnabled; } @@ -616,15 +520,17 @@ protected: static std::unique_ptr Create( TYPE aType ); SIM_MODEL( TYPE aType ); - SIM_MODEL( TYPE aType, std::unique_ptr aSpiceGenerator ); + SIM_MODEL( TYPE aType, + std::unique_ptr aSpiceGenerator ); + SIM_MODEL( TYPE aType, + std::unique_ptr aSpiceGenerator, + std::unique_ptr aSerde ); virtual void CreatePins( unsigned aSymbolPinCount ); template void WriteInferredDataFields( std::vector& aFields, const std::string& aValue = "" ) const; - virtual std::string GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const; - std::string GenerateValueField( const std::string& aPairSeparator ) const; std::string GenerateParamsField( const std::string& aPairSeparator ) const; void ParseParamsField( const std::string& aParamsField ); @@ -636,6 +542,7 @@ protected: void InferredReadDataFields( unsigned aSymbolPinCount, const std::vector* aFields ); std::vector m_params; const SIM_MODEL* m_baseModel; + std::unique_ptr m_serde; private: static TYPE readTypeFromSpiceStrings( const std::string& aTypeString, @@ -658,7 +565,6 @@ private: std::string parseFieldFloatValue( std::string aFieldFloatValue ); - virtual bool hasPrimaryValue() const { return false; } virtual bool requiresSpiceModelLine() const; virtual std::vector getPinNames() const { return {}; } diff --git a/eeschema/sim/sim_model_behavioral.cpp b/eeschema/sim/sim_model_behavioral.cpp index 2140ff1932..23bb696728 100644 --- a/eeschema/sim/sim_model_behavioral.cpp +++ b/eeschema/sim/sim_model_behavioral.cpp @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -149,9 +150,9 @@ void SIM_MODEL_BEHAVIORAL::inferredReadDataFields( unsigned aSymbolPinCount, { ParsePinsField( aSymbolPinCount, GetFieldValue( aFields, PINS_FIELD ) ); - if( ( InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ), - GetFieldValue( aFields, VALUE_FIELD ), - aSymbolPinCount ) == GetType() + if( ( m_serde->InferTypeFromRefAndValue( GetFieldValue( aFields, REFERENCE_FIELD ), + GetFieldValue( aFields, VALUE_FIELD ), + aSymbolPinCount ) == GetType() && parseValueField( GetFieldValue( aFields, VALUE_FIELD ) ) ) // If Value is device type, this is an empty model || GetFieldValue( aFields, VALUE_FIELD ) == DeviceTypeInfo( GetDeviceType() ).fieldValue ) diff --git a/eeschema/sim/sim_model_ideal.cpp b/eeschema/sim/sim_model_ideal.cpp index 7bdc770867..51d6894077 100644 --- a/eeschema/sim/sim_model_ideal.cpp +++ b/eeschema/sim/sim_model_ideal.cpp @@ -59,8 +59,8 @@ SIM_MODEL_IDEAL::SIM_MODEL_IDEAL( TYPE aType ) : SIM_MODEL( aType, std::make_unique( *this ) ) { static PARAM::INFO resistor = makeParamInfo( "r", "Resistance", "Ω" ); - static PARAM::INFO capacitor = makeParamInfo( "c", "Capacitance", "F" ); - static PARAM::INFO inductor = makeParamInfo( "l", "Inductance", "H" ); + static PARAM::INFO capacitor = makeParamInfo( "c", "Capacitance", "F" ); + static PARAM::INFO inductor = makeParamInfo( "l", "Inductance", "H" ); switch( aType ) { diff --git a/eeschema/sim/sim_model_ideal.h b/eeschema/sim/sim_model_ideal.h index ad2023b55d..d404330a40 100644 --- a/eeschema/sim/sim_model_ideal.h +++ b/eeschema/sim/sim_model_ideal.h @@ -50,12 +50,12 @@ public: void WriteDataLibFields( std::vector& aFields ) const override; const PARAM* GetTunerParam() const override { return &GetParam( 0 ); } + bool HasPrimaryValue() const override { return true; } private: template void inferredWriteDataFields( std::vector& aFields ) const; - bool hasPrimaryValue() const override { return true; } std::vector getPinNames() const override { return { "+", "-" }; } static PARAM::INFO makeParamInfo( std::string aName, std::string aDescription, std::string aUnit ); diff --git a/eeschema/sim/sim_model_r_pot.h b/eeschema/sim/sim_model_r_pot.h index 5c4a481895..4cbd06a98a 100644 --- a/eeschema/sim/sim_model_r_pot.h +++ b/eeschema/sim/sim_model_r_pot.h @@ -49,13 +49,13 @@ public: void WriteDataLibFields( std::vector& aFields ) const override; const PARAM* GetTunerParam() const override { return FindParam( "pos" ); } + bool HasPrimaryValue() const override { return true; } private: template void inferredWriteDataFields( std::vector& aFields ) const; std::vector getPinNames() const override { return { "+", "wiper", "-" }; } - bool hasPrimaryValue() const override { return true; } static const std::vector makeParamInfos(); }; diff --git a/eeschema/sim/sim_model_raw_spice.cpp b/eeschema/sim/sim_model_raw_spice.cpp index 4941619540..fef3089d6d 100644 --- a/eeschema/sim/sim_model_raw_spice.cpp +++ b/eeschema/sim/sim_model_raw_spice.cpp @@ -23,6 +23,7 @@ */ #include +#include #include #include @@ -32,7 +33,7 @@ namespace SIM_MODEL_RAW_SPICE_PARSER { - using namespace SIM_MODEL_GRAMMAR; + using namespace SIM_SERDE_GRAMMAR; template struct legacyPinSequenceSelector : std::false_type {}; template <> struct legacyPinSequenceSelector : std::true_type {}; diff --git a/eeschema/sim/sim_model_source.cpp b/eeschema/sim/sim_model_source.cpp index 97ceaa098f..2c09f191a1 100644 --- a/eeschema/sim/sim_model_source.cpp +++ b/eeschema/sim/sim_model_source.cpp @@ -198,6 +198,15 @@ std::string SPICE_GENERATOR_SOURCE::getParamValueString( const std::string& aPar } +std::string SIM_SERDE_SOURCE::GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const +{ + if( aParam.value->ToString() == "0" ) + return ""; + + return SIM_SERDE::GenerateParamValuePair( aParam ); +} + + SIM_MODEL_SOURCE::SIM_MODEL_SOURCE( TYPE aType ) : SIM_MODEL( aType, std::make_unique( *this ) ) { @@ -253,15 +262,6 @@ void SIM_MODEL_SOURCE::SetParamValue( int aParamIndex, const SIM_VALUE& aValue ) } -std::string SIM_MODEL_SOURCE::GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const -{ - if( aParam.value->ToString() == "0" ) - return ""; - - return SIM_MODEL::GenerateParamValuePair( aParam, aIsFirst ); -} - - template void SIM_MODEL_SOURCE::inferredWriteDataFields( std::vector& aFields ) const { diff --git a/eeschema/sim/sim_model_source.h b/eeschema/sim/sim_model_source.h index c7fb0dc503..ef3605acb7 100644 --- a/eeschema/sim/sim_model_source.h +++ b/eeschema/sim/sim_model_source.h @@ -26,12 +26,13 @@ #define SIM_MODEL_SOURCE_H #include +#include #include namespace SIM_MODEL_SOURCE_GRAMMAR { - using namespace SIM_MODEL_GRAMMAR; + using namespace SIM_SERDE_GRAMMAR; struct pwlSep : plus {}; struct pwlValues : seq>, @@ -54,7 +55,14 @@ public: private: std::string getParamValueString( const std::string& aParamName, - const std::string& aDefaultValue ) const; + const std::string& aDefaultValue ) const; +}; + + +class SIM_SERDE_SOURCE : public SIM_SERDE +{ +protected: + std::string GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const override; }; @@ -69,16 +77,13 @@ public: void SetParamValue( int aParamIndex, const SIM_VALUE& aValue ) override; bool HasAutofill() const override { return true; } - -protected: - std::string GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const override; + bool HasPrimaryValue() const override { return true; } private: template void inferredWriteDataFields( std::vector& aFields ) const; - bool hasPrimaryValue() const override { return true; } std::vector getPinNames() const override { return { "+", "-" }; } static const std::vector& makeParamInfos( TYPE aType ); diff --git a/eeschema/sim/sim_model_switch.cpp b/eeschema/sim/sim_model_switch.cpp index 2027801e9b..84dce4f3c4 100644 --- a/eeschema/sim/sim_model_switch.cpp +++ b/eeschema/sim/sim_model_switch.cpp @@ -94,8 +94,19 @@ std::vector> SPICE_GENERATOR_SWITCH } +std::string SIM_SERDE_SWITCH::GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const +{ + if( aParam.info.name == "ic" && aParam.value->ToString() == "none" ) + return ""; + + return SIM_SERDE::GenerateParamValuePair( aParam ); +} + + SIM_MODEL_SWITCH::SIM_MODEL_SWITCH( TYPE aType ) : - SIM_MODEL( aType, std::make_unique( *this ) ) + SIM_MODEL( aType, + std::make_unique( *this ), + std::make_unique( *this ) ) { static std::vector vsw = makeSwVParamInfos(); static std::vector isw = makeSwIParamInfos(); @@ -121,15 +132,6 @@ SIM_MODEL_SWITCH::SIM_MODEL_SWITCH( TYPE aType ) : } -std::string SIM_MODEL_SWITCH::GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const -{ - if( aParam.info.name == "ic" && aParam.value->ToString() == "none" ) - { - return ""; - } - - return SIM_MODEL::GenerateParamValuePair( aParam, aIsFirst ); -} const std::vector SIM_MODEL_SWITCH::makeSwVParamInfos() diff --git a/eeschema/sim/sim_model_switch.h b/eeschema/sim/sim_model_switch.h index 1e0135ee89..1158c77bc8 100644 --- a/eeschema/sim/sim_model_switch.h +++ b/eeschema/sim/sim_model_switch.h @@ -27,6 +27,7 @@ #include #include +#include class SPICE_GENERATOR_SWITCH : public SPICE_GENERATOR @@ -40,14 +41,21 @@ public: }; +class SIM_SERDE_SWITCH : public SIM_SERDE +{ +public: + using SIM_SERDE::SIM_SERDE; + +protected: + std::string GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const override; +}; + + class SIM_MODEL_SWITCH : public SIM_MODEL { public: SIM_MODEL_SWITCH( TYPE aType ); -protected: - std::string GenerateParamValuePair( const PARAM& aParam, bool& aIsFirst ) const override; - private: std::vector getPinNames() const override { diff --git a/eeschema/sim/sim_serde.cpp b/eeschema/sim/sim_serde.cpp new file mode 100644 index 0000000000..48b5e53ce4 --- /dev/null +++ b/eeschema/sim/sim_serde.cpp @@ -0,0 +1,364 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mikolaj Wielgus + * Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * https://www.gnu.org/licenses/gpl-3.0.html + * or you may search the http://www.gnu.org website for the version 3 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include +#include +#include +#include +#include + + +namespace SIM_SERDE_PARSER +{ + using namespace SIM_SERDE_GRAMMAR; + + template struct fieldParamValuePairsSelector : std::false_type {}; + template <> struct fieldParamValuePairsSelector : std::true_type {}; + template <> struct fieldParamValuePairsSelector : std::true_type {}; + template <> struct fieldParamValuePairsSelector : std::true_type {}; + + + template struct pinSequenceSelector : std::false_type {}; + template <> struct pinSequenceSelector : std::true_type {}; + + template struct fieldInferValueSelector : std::false_type {}; + template <> struct fieldInferValueSelector : std::true_type {}; + template <> struct fieldInferValueSelector : std::true_type {}; + template <> struct fieldInferValueSelector> : std::true_type {}; + template <> struct fieldInferValueSelector : std::true_type {}; +} + + +std::string SIM_SERDE::GenerateDevice() const +{ + return m_model.GetDeviceTypeInfo().fieldValue; +} + + +std::string SIM_SERDE::GenerateType() const +{ + return m_model.GetTypeInfo().fieldValue; +} + + +std::string SIM_SERDE::GenerateValue() const +{ + std::string result; + + for( int i = 0; i < m_model.GetParamCount(); ++i ) + { + const SIM_MODEL::PARAM& param = m_model.GetParam( i ); + + if( i == 0 && m_model.HasPrimaryValue() ) + { + result.append( param.value->ToString() ); + continue; + } + + if( param.value->ToString() == "" ) + continue; + + result.append( fmt::format( " {}", GenerateParamValuePair( param ) ) ); + } + + if( result == "" ) + result = m_model.GetDeviceTypeInfo().fieldValue; + + return result; +} + + +std::string SIM_SERDE::GenerateParams() const +{ + std::string result; + + for( int i = 0; i < m_model.GetParamCount(); ++i ) + { + const SIM_MODEL::PARAM& param = m_model.GetParam( i ); + + if( param.value->ToString() == "" ) + continue; + + if( i != 0 ) + result.append( " " ); + + result.append( GenerateParamValuePair( param ) ); + } + + return result; +} + + +std::string SIM_SERDE::GeneratePins() const +{ + std::string result; + + for( int i = 0; i < m_model.GetPinCount(); ++i ) + { + const SIM_MODEL::PIN& pin = m_model.GetPin( i ); + + if( i != 0 ) + result.append( " " ); + + if( pin.symbolPinNumber == "" ) + result.append( "~" ); + else + result.append( pin.symbolPinNumber ); + } + + return result; +} + + +std::string SIM_SERDE::GenerateEnable() const +{ + return m_model.IsEnabled() ? "" : "0"; +} + + +SIM_MODEL::TYPE SIM_SERDE::ParseDeviceAndType( const std::string& aDevice, + const std::string& aType ) +{ + for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() ) + { + if( aType == SIM_MODEL::TypeInfo( type ).fieldValue + && aDevice == SIM_MODEL::DeviceTypeInfo( SIM_MODEL::TypeInfo( type ).deviceType ).fieldValue ) + { + return type; + } + } + + return SIM_MODEL::TYPE::NONE; +} + + +void SIM_SERDE::ParseValue( const std::string& aValue ) +{ + try + { + // TODO: Don't call this multiple times. + tao::pegtl::string_input<> in( aValue, SIM_MODEL::VALUE_FIELD ); + auto root = tao::pegtl::parse_tree::parse( in ); + + for( const auto& node : root->children ) + { + if( node->is_type() ) + { + if( m_model.HasPrimaryValue() ) + { + for( const auto& subnode : node->children ) + { + if( subnode->is_type>() ) + { + m_model.SetParamValue( 0, subnode->string() ); + } + } + } + else + { + THROW_IO_ERROR( + wxString::Format( _( "Simulation model of type '%s' cannot have a primary value (which is '%s') in Value field" ), + m_model.GetTypeInfo().fieldValue, + node->string() ) ); + } + } + else if( node->is_type() ) + ParseParams( node->string() ); + } + } + catch( const tao::pegtl::parse_error& e ) + { + THROW_IO_ERROR( e.what() ); + } +} + + +void SIM_SERDE::ParseParams( const std::string& aParams ) +{ + tao::pegtl::string_input<> in( aParams, SIM_MODEL::PARAMS_FIELD ); + std::unique_ptr root; + + try + { + // Using parse tree instead of actions because we don't care about performance that much, + // and having a tree greatly simplifies things. + root = tao::pegtl::parse_tree::parse< + SIM_SERDE_PARSER::fieldParamValuePairsGrammar, + SIM_SERDE_PARSER::fieldParamValuePairsSelector, + tao::pegtl::nothing, + SIM_SERDE_PARSER::control> + ( in ); + } + catch( const tao::pegtl::parse_error& e ) + { + THROW_IO_ERROR( e.what() ); + } + + std::string paramName; + + for( const auto& node : root->children ) + { + if( node->is_type() ) + paramName = node->string(); + // TODO: Do something with number. + // It doesn't seem too useful? + else if( node->is_type() + || node->is_type() ) + { + wxASSERT( paramName != "" ); + // TODO: Shouldn't be named "...fromSpiceCode" here... + + m_model.SetParamValue( paramName, node->string(), SIM_VALUE_GRAMMAR::NOTATION::SI ); + } + else if( node->is_type() ) + { + std::string str = node->string(); + + // Unescape quotes. + boost::replace_all( str, "\\\"", "\"" ); + + m_model.SetParamValue( paramName, str, SIM_VALUE_GRAMMAR::NOTATION::SI ); + } + else + { + wxFAIL; + } + } +} + + +void SIM_SERDE::ParsePins( const std::string& aPins ) +{ + if( aPins == "" ) + return; + + tao::pegtl::string_input<> in( aPins, PINS_FIELD ); + std::unique_ptr root; + + try + { + root = tao::pegtl::parse_tree::parse( in ); + } + catch( const tao::pegtl::parse_error& e ) + { + THROW_IO_ERROR( e.what() ); + } + + if( static_cast( root->children.size() ) != m_model.GetPinCount() ) + { + THROW_IO_ERROR( wxString::Format( _( "%s describes %lu pins, expected %u" ), + PINS_FIELD, + root->children.size(), + m_model.GetPinCount() ) ); + } + + for( int pinIndex = 0; pinIndex < static_cast( root->children.size() ); ++pinIndex ) + { + if( root->children.at( pinIndex )->string() == "~" ) + m_model.SetPinSymbolPinNumber( pinIndex, "" ); + else + m_model.SetPinSymbolPinNumber( pinIndex, root->children.at( pinIndex )->string() ); + } +} + + +void SIM_SERDE::ParseEnable( const std::string& aEnable ) +{ + if( aEnable == "" ) + return; + + char c = boost::to_lower_copy( aEnable )[0]; + + if( c == 'n' || c == 'f' || c == '0' ) + m_model.SetIsEnabled( false ); +} + + +SIM_MODEL::TYPE SIM_SERDE::InferTypeFromRefAndValue( const std::string& aRef, + const std::string& aValue, + int aSymbolPinCount ) +{ + std::string typeString; + + try + { + tao::pegtl::string_input<> in( aValue, VALUE_FIELD ); + auto root = tao::pegtl::parse_tree::parse( in ); + + for( const auto& node : root->children ) + { + if( node->is_type() ) + typeString = node->string(); + } + } + catch( const tao::pegtl::parse_error& ) + { + } + + SIM_MODEL::DEVICE_TYPE_ deviceType = SIM_MODEL::InferDeviceTypeFromRef( aRef ); + + // Exception. Potentiometer model is determined from pin count. + if( deviceType == SIM_MODEL::DEVICE_TYPE_::R && aSymbolPinCount == 3 ) + return SIM_MODEL::TYPE::R_POT; + + for( SIM_MODEL::TYPE type : SIM_MODEL::TYPE_ITERATOR() ) + { + if( SIM_MODEL::TypeInfo( type ).deviceType == deviceType + && SIM_MODEL::TypeInfo( type ).fieldValue == typeString ) + { + return type; + } + } + + return SIM_MODEL::TYPE::NONE; +} + + +std::string SIM_SERDE::GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const +{ + std::string name = aParam.info.name; + + // Because of collisions with instance parameters, we append some model parameters with "_". + if( boost::ends_with( aParam.info.name, "_" ) ) + name = aParam.info.name.substr( 0, aParam.info.name.length() - 1 ); + + std::string value = aParam.value->ToString(); + + if( value.find( " " ) != std::string::npos ) + value = fmt::format( "\"{}\"", value ); + + return fmt::format( "{}={}", aParam.info.name, value ); +} diff --git a/eeschema/sim/sim_serde.h b/eeschema/sim/sim_serde.h new file mode 100644 index 0000000000..e01829108b --- /dev/null +++ b/eeschema/sim/sim_serde.h @@ -0,0 +1,169 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2022 Mikolaj Wielgus + * Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * https://www.gnu.org/licenses/gpl-3.0.html + * or you may search the http://www.gnu.org website for the version 3 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef SIM_SERDE_H +#define SIM_SERDE_H + +#include + + +namespace SIM_SERDE_GRAMMAR +{ + using namespace SIM_VALUE_GRAMMAR; + + struct sep : plus {}; + + + struct legacyPinNumber : digits {}; + struct legacyPinSequence : list {}; + + struct legacyPinSequenceGrammar : must {}; + + + struct pinNumber : plus, any> {}; + struct pinSequence : list {}; + struct pinSequenceGrammar : must, + opt, + opt, + tao::pegtl::eof> {}; + + struct param : plus {}; + + struct unquotedString : plus, any> {}; + struct quotedStringContent : star>, any> {}; // TODO: Allow escaping '"'. + struct quotedString : seq, + quotedStringContent, + one<'"'>> {}; + + struct fieldParamValuePair : if_must, + one<'='>, + opt, + sor> {}; + struct fieldParamValuePairs : list {}; + struct fieldParamValuePairsGrammar : must, + opt, + opt, + tao::pegtl::eof> {}; + + struct fieldInferValueType : plus {}; + struct fieldInferValuePrimaryValue : seq>, + tao::pegtl::digit>>, + // END HACK. + number, + // Hackish: match anything until param-value pairs. + // Because the user may want to write something like + // "10k 30% 30mW w=0.4", but we care only about the + // "10k" and "w=0.4". + star>, + any>> {}; + struct fieldInferValue : sor>, + seq, + opt, + opt>> {}; + struct fieldInferValueGrammar : must, + fieldInferValue, + opt, + tao::pegtl::eof> {}; + + + template inline constexpr const char* errorMessage = nullptr; + template <> inline constexpr auto errorMessage> = ""; + template <> inline constexpr auto errorMessage> = ""; + template <> inline constexpr auto errorMessage>> = ""; + template <> inline constexpr auto errorMessage> = + "expected '='"; + template <> inline constexpr auto errorMessage> = + "expected quoted or unquoted string"; + template <> inline constexpr auto errorMessage = + "expected parameter=value pairs"; + template <> inline constexpr auto errorMessage> = ""; + template <> inline constexpr auto errorMessage = + "expected parameter=value pairs, together possibly preceded by a type or primary value"; + template <> inline constexpr auto errorMessage = + "expected end of string"; + + struct error + { + template static constexpr bool raise_on_failure = false; + template static constexpr auto message = errorMessage; + }; + + template using control = must_if::control; +} + + +/** + * SerDe = Serializer-Deserializer + */ +class SIM_SERDE +{ +public: + static constexpr auto REFERENCE_FIELD = "Reference"; + static constexpr auto VALUE_FIELD = "Value"; + + static constexpr auto DEVICE_TYPE_FIELD = "Sim_Device"; + static constexpr auto TYPE_FIELD = "Sim_Type"; + static constexpr auto PINS_FIELD = "Sim_Pins"; + static constexpr auto PARAMS_FIELD = "Sim_Params"; + static constexpr auto ENABLE_FIELD = "Sim_Enable"; + + SIM_SERDE( SIM_MODEL& aModel ) : m_model( aModel ) {} + + std::string GenerateDevice() const; + std::string GenerateType() const; + std::string GenerateValue() const; + std::string GenerateParams() const; + std::string GeneratePins() const; + std::string GenerateEnable() const; + + SIM_MODEL::TYPE ParseDeviceAndType( const std::string& aDevice, + const std::string& aType ); + void ParseValue( const std::string& aValue ); + void ParseParams( const std::string& aParams ); + void ParsePins( const std::string& aPins ); + void ParseEnable( const std::string& aEnable ); + + static SIM_MODEL::TYPE InferTypeFromRefAndValue( const std::string& aRef, + const std::string& aValue, + int aSymbolPinCount ); + +protected: + virtual std::string GenerateParamValuePair( const SIM_MODEL::PARAM& aParam ) const; + +private: + SIM_MODEL& m_model; +}; + +#endif // SIM_SERDE_H