Move (de)serialization of models into fields to new SIM_SERDE class

This commit is contained in:
Mikolaj Wielgus 2022-11-11 03:07:34 +01:00
parent f846bc833b
commit 490069c5e6
14 changed files with 628 additions and 401 deletions

View File

@ -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

View File

@ -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 <typename Rule> struct fieldParamValuePairsSelector : std::false_type {};
template <> struct fieldParamValuePairsSelector<param> : std::true_type {};
template <> struct fieldParamValuePairsSelector<quotedStringContent> : std::true_type {};
template <> struct fieldParamValuePairsSelector<unquotedString> : std::true_type {};
template <typename Rule> struct pinSequenceSelector : std::false_type {};
template <> struct pinSequenceSelector<pinNumber> : std::true_type {};
template <typename Rule> struct fieldInferValueSelector : std::false_type {};
template <> struct fieldInferValueSelector<fieldInferValueType> : std::true_type {};
template <> struct fieldInferValueSelector<fieldInferValuePrimaryValue> : std::true_type {};
template <> struct fieldInferValueSelector<number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>> : std::true_type {};
template <> struct fieldInferValueSelector<fieldParamValuePairs> : 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<T>& 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<SIM_MODEL_PARSER::fieldInferValueGrammar,
SIM_MODEL_PARSER::fieldInferValueSelector,
tao::pegtl::nothing,
SIM_MODEL_PARSER::control>( in );
for( const auto& node : root->children )
{
if( node->is_type<SIM_MODEL_PARSER::fieldInferValueType>() )
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 <typename T>
TYPE SIM_MODEL::InferTypeFromLegacyFields( const std::vector<T>& aFields )
{
@ -967,17 +907,30 @@ std::unique_ptr<SIM_MODEL> SIM_MODEL::Create( TYPE aType )
SIM_MODEL::SIM_MODEL( TYPE aType ) :
SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR>( *this ) )
SIM_MODEL( aType,
std::make_unique<SPICE_GENERATOR>( *this ),
std::make_unique<SIM_SERDE>( *this ) )
{
}
SIM_MODEL::SIM_MODEL( TYPE aType,
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator ) :
SIM_MODEL( aType,
std::move( aSpiceGenerator ),
std::make_unique<SIM_SERDE>( *this ) )
{
}
SIM_MODEL::SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> 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<SPICE_GENERATOR> aSpiceGenerator,
std::unique_ptr<SIM_SERDE> 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<T>& 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<tao::pegtl::parse_tree::node> 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<SIM_MODEL_PARSER::param>() )
paramName = node->string();
// TODO: Do something with number<SIM_VALUE::TYPE_INT, ...>.
// It doesn't seem too useful?
else if( node->is_type<SIM_MODEL_PARSER::quotedStringContent>()
|| node->is_type<SIM_MODEL_PARSER::unquotedString>() )
{
wxASSERT( paramName != "" );
// TODO: Shouldn't be named "...fromSpiceCode" here...
SetParamValue( paramName, node->string(), SIM_VALUE_GRAMMAR::NOTATION::SI );
}
else if( node->is_type<SIM_MODEL_PARSER::quotedString>() )
{
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<tao::pegtl::parse_tree::node> root;
try
{
root = tao::pegtl::parse_tree::parse<SIM_MODEL_PARSER::pinSequenceGrammar,
SIM_MODEL_PARSER::pinSequenceSelector,
tao::pegtl::nothing,
SIM_MODEL_PARSER::control>( in );
}
catch( const tao::pegtl::parse_error& e )
{
THROW_IO_ERROR( e.what() );
}
if( static_cast<int>( 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<int>( 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<SIM_MODEL_PARSER::fieldInferValueGrammar,
SIM_MODEL_PARSER::fieldInferValueSelector,
tao::pegtl::nothing,
SIM_MODEL_PARSER::control>( in );
for( const auto& node : root->children )
{
if( node->is_type<SIM_MODEL_PARSER::fieldInferValuePrimaryValue>() )
{
if( hasPrimaryValue() )
{
for( const auto& subnode : node->children )
{
if( subnode->is_type<SIM_MODEL_PARSER::number<SIM_VALUE::TYPE_FLOAT,
SIM_VALUE::NOTATION::SI>>() )
{
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<SIM_MODEL_PARSER::fieldParamValuePairs>() )
ParseParamsField( node->string() );
}
}
catch( const tao::pegtl::parse_error& e )
{
THROW_IO_ERROR( e.what() );
}
m_serde->ParseValue( GetFieldValue( aFields, VALUE_FIELD ) );
SetIsInferred( true );
}

View File

@ -39,102 +39,7 @@
class SIM_LIBRARY;
class SPICE_GENERATOR;
namespace SIM_MODEL_GRAMMAR
{
using namespace SIM_VALUE_GRAMMAR;
struct sep : plus<space> {};
struct legacyPinNumber : digits {};
struct legacyPinSequence : list<legacyPinNumber, sep> {};
struct legacyPinSequenceGrammar : must<legacyPinSequence,
tao::pegtl::eof> {};
struct pinNumber : plus<not_at<sep>, any> {};
struct pinSequence : list<pinNumber, sep> {};
struct pinSequenceGrammar : must<opt<sep>,
opt<pinSequence>,
opt<sep>,
tao::pegtl::eof> {};
struct param : plus<alnum> {};
struct unquotedString : plus<not_at<sep>, any> {};
struct quotedStringContent : star<not_at<one<'"'>>, any> {}; // TODO: Allow escaping '"'.
struct quotedString : seq<one<'"'>,
quotedStringContent,
one<'"'>> {};
struct fieldParamValuePair : if_must<param,
opt<sep>,
one<'='>,
opt<sep>,
sor<quotedString,
unquotedString>> {};
struct fieldParamValuePairs : list<fieldParamValuePair, sep> {};
struct fieldParamValuePairsGrammar : must<opt<sep>,
opt<fieldParamValuePairs>,
opt<sep>,
tao::pegtl::eof> {};
struct fieldInferValueType : plus<upper> {};
struct fieldInferValuePrimaryValue : seq<// HACK: Because `number` matches empty string,
// ensure it is not empty.
at<sor<tao::pegtl::digit,
seq<one<'.'>>,
tao::pegtl::digit>>,
// END HACK.
number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>,
// 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<not_at<sep,
try_catch<fieldParamValuePairs>>,
any>> {};
struct fieldInferValue : sor<seq<fieldInferValueType,
opt<sep,
fieldParamValuePairs>>,
seq<opt<fieldInferValuePrimaryValue>,
opt<sep>,
opt<fieldParamValuePairs>>> {};
struct fieldInferValueGrammar : must<opt<sep>,
fieldInferValue,
opt<sep>,
tao::pegtl::eof> {};
template <typename> inline constexpr const char* errorMessage = nullptr;
template <> inline constexpr auto errorMessage<opt<sep>> = "";
template <> inline constexpr auto errorMessage<opt<pinSequence>> = "";
template <> inline constexpr auto errorMessage<opt<sor<fieldInferValueType,
fieldInferValuePrimaryValue>>> = "";
template <> inline constexpr auto errorMessage<one<'='>> =
"expected '='";
template <> inline constexpr auto errorMessage<sor<quotedString,
unquotedString>> =
"expected quoted or unquoted string";
template <> inline constexpr auto errorMessage<fieldParamValuePairs> =
"expected parameter=value pairs";
template <> inline constexpr auto errorMessage<opt<fieldParamValuePairs>> = "";
template <> inline constexpr auto errorMessage<fieldInferValue> =
"expected parameter=value pairs, together possibly preceded by a type or primary value";
template <> inline constexpr auto errorMessage<tao::pegtl::eof> =
"expected end of string";
struct error
{
template <typename Rule> static constexpr bool raise_on_failure = false;
template <typename Rule> static constexpr auto message = errorMessage<Rule>;
};
template <typename Rule> using control = must_if<error>::control<Rule>;
}
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 <typename T>
static TYPE InferTypeFromLegacyFields( const std::vector<T>& 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<SIM_MODEL> Create( TYPE aType );
SIM_MODEL( TYPE aType );
SIM_MODEL( TYPE aType, std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator );
SIM_MODEL( TYPE aType,
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator );
SIM_MODEL( TYPE aType,
std::unique_ptr<SPICE_GENERATOR> aSpiceGenerator,
std::unique_ptr<SIM_SERDE> aSerde );
virtual void CreatePins( unsigned aSymbolPinCount );
template <typename T>
void WriteInferredDataFields( std::vector<T>& 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<T>* aFields );
std::vector<PARAM> m_params;
const SIM_MODEL* m_baseModel;
std::unique_ptr<SIM_SERDE> 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<std::string> getPinNames() const { return {}; }

View File

@ -23,6 +23,7 @@
*/
#include <sim/sim_model_behavioral.h>
#include <sim/sim_serde.h>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/trim.hpp>
@ -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 )

View File

@ -59,8 +59,8 @@ SIM_MODEL_IDEAL::SIM_MODEL_IDEAL( TYPE aType ) :
SIM_MODEL( aType, std::make_unique<SPICE_GENERATOR_IDEAL>( *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 )
{

View File

@ -50,12 +50,12 @@ public:
void WriteDataLibFields( std::vector<LIB_FIELD>& aFields ) const override;
const PARAM* GetTunerParam() const override { return &GetParam( 0 ); }
bool HasPrimaryValue() const override { return true; }
private:
template <typename T>
void inferredWriteDataFields( std::vector<T>& aFields ) const;
bool hasPrimaryValue() const override { return true; }
std::vector<std::string> getPinNames() const override { return { "+", "-" }; }
static PARAM::INFO makeParamInfo( std::string aName, std::string aDescription, std::string aUnit );

View File

@ -49,13 +49,13 @@ public:
void WriteDataLibFields( std::vector<LIB_FIELD>& aFields ) const override;
const PARAM* GetTunerParam() const override { return FindParam( "pos" ); }
bool HasPrimaryValue() const override { return true; }
private:
template <typename T>
void inferredWriteDataFields( std::vector<T>& aFields ) const;
std::vector<std::string> getPinNames() const override { return { "+", "wiper", "-" }; }
bool hasPrimaryValue() const override { return true; }
static const std::vector<PARAM::INFO> makeParamInfos();
};

View File

@ -23,6 +23,7 @@
*/
#include <sim/sim_model_raw_spice.h>
#include <sim/sim_serde.h>
#include <boost/algorithm/string/predicate.hpp>
#include <fmt/core.h>
@ -32,7 +33,7 @@
namespace SIM_MODEL_RAW_SPICE_PARSER
{
using namespace SIM_MODEL_GRAMMAR;
using namespace SIM_SERDE_GRAMMAR;
template <typename Rule> struct legacyPinSequenceSelector : std::false_type {};
template <> struct legacyPinSequenceSelector<legacyPinNumber> : std::true_type {};

View File

@ -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<SPICE_GENERATOR_SOURCE>( *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 <typename T>
void SIM_MODEL_SOURCE::inferredWriteDataFields( std::vector<T>& aFields ) const
{

View File

@ -26,12 +26,13 @@
#define SIM_MODEL_SOURCE_H
#include <sim/sim_model.h>
#include <sim/sim_serde.h>
#include <sim/spice_generator.h>
namespace SIM_MODEL_SOURCE_GRAMMAR
{
using namespace SIM_MODEL_GRAMMAR;
using namespace SIM_SERDE_GRAMMAR;
struct pwlSep : plus<space> {};
struct pwlValues : seq<opt<number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>>,
@ -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 <typename T>
void inferredWriteDataFields( std::vector<T>& aFields ) const;
bool hasPrimaryValue() const override { return true; }
std::vector<std::string> getPinNames() const override { return { "+", "-" }; }
static const std::vector<PARAM::INFO>& makeParamInfos( TYPE aType );

View File

@ -94,8 +94,19 @@ std::vector<std::reference_wrapper<const SIM_MODEL::PIN>> 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<SPICE_GENERATOR_SWITCH>( *this ) )
SIM_MODEL( aType,
std::make_unique<SPICE_GENERATOR_SWITCH>( *this ),
std::make_unique<SIM_SERDE_SWITCH>( *this ) )
{
static std::vector<PARAM::INFO> vsw = makeSwVParamInfos();
static std::vector<PARAM::INFO> 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::PARAM::INFO> SIM_MODEL_SWITCH::makeSwVParamInfos()

View File

@ -27,6 +27,7 @@
#include <sim/sim_model.h>
#include <sim/spice_generator.h>
#include <sim/sim_serde.h>
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<std::string> getPinNames() const override
{

364
eeschema/sim/sim_serde.cpp Normal file
View File

@ -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 <sim/sim_serde.h>
#include <fmt/core.h>
#include <pegtl.hpp>
#include <pegtl/contrib/parse_tree.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
namespace SIM_SERDE_PARSER
{
using namespace SIM_SERDE_GRAMMAR;
template <typename Rule> struct fieldParamValuePairsSelector : std::false_type {};
template <> struct fieldParamValuePairsSelector<param> : std::true_type {};
template <> struct fieldParamValuePairsSelector<quotedStringContent> : std::true_type {};
template <> struct fieldParamValuePairsSelector<unquotedString> : std::true_type {};
template <typename Rule> struct pinSequenceSelector : std::false_type {};
template <> struct pinSequenceSelector<pinNumber> : std::true_type {};
template <typename Rule> struct fieldInferValueSelector : std::false_type {};
template <> struct fieldInferValueSelector<fieldInferValueType> : std::true_type {};
template <> struct fieldInferValueSelector<fieldInferValuePrimaryValue> : std::true_type {};
template <> struct fieldInferValueSelector<number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>> : std::true_type {};
template <> struct fieldInferValueSelector<fieldParamValuePairs> : 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<SIM_SERDE_PARSER::fieldInferValueGrammar,
SIM_SERDE_PARSER::fieldInferValueSelector,
tao::pegtl::nothing,
SIM_SERDE_PARSER::control>( in );
for( const auto& node : root->children )
{
if( node->is_type<SIM_SERDE_PARSER::fieldInferValuePrimaryValue>() )
{
if( m_model.HasPrimaryValue() )
{
for( const auto& subnode : node->children )
{
if( subnode->is_type<SIM_SERDE_PARSER::number<SIM_VALUE::TYPE_FLOAT,
SIM_VALUE::NOTATION::SI>>() )
{
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<SIM_SERDE_PARSER::fieldParamValuePairs>() )
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<tao::pegtl::parse_tree::node> 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<SIM_SERDE_PARSER::param>() )
paramName = node->string();
// TODO: Do something with number<SIM_VALUE::TYPE_INT, ...>.
// It doesn't seem too useful?
else if( node->is_type<SIM_SERDE_PARSER::quotedStringContent>()
|| node->is_type<SIM_SERDE_PARSER::unquotedString>() )
{
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<SIM_SERDE_PARSER::quotedString>() )
{
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<tao::pegtl::parse_tree::node> root;
try
{
root = tao::pegtl::parse_tree::parse<SIM_SERDE_PARSER::pinSequenceGrammar,
SIM_SERDE_PARSER::pinSequenceSelector,
tao::pegtl::nothing,
SIM_SERDE_PARSER::control>( in );
}
catch( const tao::pegtl::parse_error& e )
{
THROW_IO_ERROR( e.what() );
}
if( static_cast<int>( 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<int>( 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<SIM_SERDE_PARSER::fieldInferValueGrammar,
SIM_SERDE_PARSER::fieldInferValueSelector,
tao::pegtl::nothing,
SIM_SERDE_PARSER::control>( in );
for( const auto& node : root->children )
{
if( node->is_type<SIM_SERDE_PARSER::fieldInferValueType>() )
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 );
}

169
eeschema/sim/sim_serde.h Normal file
View File

@ -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 <sim/sim_model.h>
namespace SIM_SERDE_GRAMMAR
{
using namespace SIM_VALUE_GRAMMAR;
struct sep : plus<space> {};
struct legacyPinNumber : digits {};
struct legacyPinSequence : list<legacyPinNumber, sep> {};
struct legacyPinSequenceGrammar : must<legacyPinSequence,
tao::pegtl::eof> {};
struct pinNumber : plus<not_at<sep>, any> {};
struct pinSequence : list<pinNumber, sep> {};
struct pinSequenceGrammar : must<opt<sep>,
opt<pinSequence>,
opt<sep>,
tao::pegtl::eof> {};
struct param : plus<alnum> {};
struct unquotedString : plus<not_at<sep>, any> {};
struct quotedStringContent : star<not_at<one<'"'>>, any> {}; // TODO: Allow escaping '"'.
struct quotedString : seq<one<'"'>,
quotedStringContent,
one<'"'>> {};
struct fieldParamValuePair : if_must<param,
opt<sep>,
one<'='>,
opt<sep>,
sor<quotedString,
unquotedString>> {};
struct fieldParamValuePairs : list<fieldParamValuePair, sep> {};
struct fieldParamValuePairsGrammar : must<opt<sep>,
opt<fieldParamValuePairs>,
opt<sep>,
tao::pegtl::eof> {};
struct fieldInferValueType : plus<upper> {};
struct fieldInferValuePrimaryValue : seq<// HACK: Because `number` matches empty string,
// ensure it is not empty.
at<sor<tao::pegtl::digit,
seq<one<'.'>>,
tao::pegtl::digit>>,
// END HACK.
number<SIM_VALUE::TYPE_FLOAT, NOTATION::SI>,
// 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<not_at<sep,
try_catch<fieldParamValuePairs>>,
any>> {};
struct fieldInferValue : sor<seq<fieldInferValueType,
opt<sep,
fieldParamValuePairs>>,
seq<opt<fieldInferValuePrimaryValue>,
opt<sep>,
opt<fieldParamValuePairs>>> {};
struct fieldInferValueGrammar : must<opt<sep>,
fieldInferValue,
opt<sep>,
tao::pegtl::eof> {};
template <typename> inline constexpr const char* errorMessage = nullptr;
template <> inline constexpr auto errorMessage<opt<sep>> = "";
template <> inline constexpr auto errorMessage<opt<pinSequence>> = "";
template <> inline constexpr auto errorMessage<opt<sor<fieldInferValueType,
fieldInferValuePrimaryValue>>> = "";
template <> inline constexpr auto errorMessage<one<'='>> =
"expected '='";
template <> inline constexpr auto errorMessage<sor<quotedString,
unquotedString>> =
"expected quoted or unquoted string";
template <> inline constexpr auto errorMessage<fieldParamValuePairs> =
"expected parameter=value pairs";
template <> inline constexpr auto errorMessage<opt<fieldParamValuePairs>> = "";
template <> inline constexpr auto errorMessage<fieldInferValue> =
"expected parameter=value pairs, together possibly preceded by a type or primary value";
template <> inline constexpr auto errorMessage<tao::pegtl::eof> =
"expected end of string";
struct error
{
template <typename Rule> static constexpr bool raise_on_failure = false;
template <typename Rule> static constexpr auto message = errorMessage<Rule>;
};
template <typename Rule> using control = must_if<error>::control<Rule>;
}
/**
* 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