我正在评估我的飞行模拟项目中代码生成的使用。更具体地说,需要允许“普通工程师”(我自己没有冒犯)来定义以比C ++提供的更自然的语法描述动态系统的微分方程。我们的想法是设计一种抽象的描述语言,可以很容易地理解和编辑它来生成C ++代码。该描述符由建模工程师提供,并由实现和维护模拟环境的人员使用以生成代码。
我有这样的想法:
model Aircraft has
state x1, x2;
state x3;
input double : u;
input bool : flag1, flag2;
algebraic double : x1x2;
model Engine : tw1, tw2;
model Gear : gear;
model ISA : isa;
trim routine HorizontalFight;
trim routine OnGround, General;
constant double : c1, c2;
constant int : ci1;
begin differential equations
x1' = x1 + 2.*x2;
x2' = x2 + x1x2;
begin algebraic equations
x1x2 = x1*x2 + x1';
end model
保留C语言的灵活性非常重要,因此描述符语言仅用于定义模型类的定义和实现的某些部分。这样,一名工程师从描述语言中提供模型,如上所示,维护工程师将添加所有代码以从文件中读取参数,启动/停止/暂停模拟的执行以及具体对象如何实例化。 / p>
我的第一个是从描述符文件生成两个文件:一个包含声明的.h文件和一个包含某些函数实现的.cpp文件。然后需要在适当的地方#include
[File Aircarft.h]
class Aircraft
{
public:
void Aircraft(..); // hand-written constructor
void ReadParameters(string &file_name); // hand-written
private:
/* more hand wirtten boiler-plate code */
/* generate declarations follow */
#include "Aircraft.generated.decl"
};
[File Aircraft.cpp]
Aircraft::Aircraft(..) { /* hand-written constructor implementation */ }
/* more hand-written implementation code */
/* generated implementation code follows */
#include "Aircraft.generated.impl"
有任何想法或建议吗?
EDIT1:
我想澄清一下,存在一个表达动态系统的微分方程和(实时)模拟的框架。问题是大多数具有领域知识的工程师对C ++的使用非常犹豫。实际上,我们希望为他们提供更轻松的方式来贡献他们的部分(数学公式),同时保留C ++的灵活性。
当然可以使用MEX编译器从MATLAB / Simulink模型生成C代码,但我们希望避免相关的许可费用。
重构整个事务以使用通用脚本语言可能超出了可用的劳动力范围。此外,就我现在和飞行中所掌握的而言,这也需要工程师学习脚本语言。我还不相信这比上面例举的语法更容易。
EDIT2:接受的答案
我接受Richard Harrison给出的答案,因为他与自定义代码生成器分享了他的经验。我还将考虑有关脚本接口的提示,尽管我处于困境中,我将没有时间将所有这些作为我的论文的一部分来实现。仍然这些指针可能有助于说服我的同事考虑一个更连贯的阶级的heriarchy。非常感谢!
答案 0 :(得分:2)
这种方法的问题是,只有在C ++编译器实际编译代码时,才会检测到数学语法中的任何错误。此时,很难提供与规范语言中的错误特定行相关的良好错误消息。因此,除非您的规范语言处理器还对规范执行语法和语义检查(即它至少是规范语言的部分编译器),否则我不会采用这种方法。
答案 1 :(得分:1)
我会采取另一种方式。在C / C ++中实现所有原始类型,组合功能,遍历,模拟和流控制功能,并添加丰富的交互式shell。
使用SWIG可以轻松完成此操作。它为应用程序添加了脚本界面,并支持许多脚本语言。
拥有丰富shell接口的模拟器,具有领域知识的工程师(而不是软件工程师)可以轻松创建用于仿真/建模的原型脚本,调试和调整这些脚本,然后,如果性能不够,瓶颈代码可以可以由软件所有者轻松地从脚本移植到本机C / C ++代码。
这种方法在大多数EDA系统(Cadence,Synopsys等)中使用,必须模拟模型中具有> 10e9单位的系统,并且已被证明是CAD软件的最佳解决方案。事实上,几乎不需要将脚本重写为母语,因为大部分时间都花在以本机语言实现的微分方程系统求解器上。
此外: 您可以查看tutorial,了解如何在C ++程序中启用脚本接口。我会选择TCL,因为它是一种非常简单但足够的脚本语言,所有命令都可以放在一个页面上。教学工程师应该没有任何问题,您只能记录一小部分功能,以模仿原始语法示例。
您的模拟脚本如下所示
package require my_models ;# load and initialize models
package require my_preconditioners ;# load and initialize preconditioners
package require my_solvers ;# load and initialize solvers
package require my_simulators ;# load simulators
set mdl [model_create "boeing_777_new"] ;# create jet model
set wing [model_load "ceramic_112233_long" "/mnt/proj/777/wing.models"] ;# load wings models
model_set_param $wing "paint" "silver_xx_233445" ;# change some parameter
model_add_element $mdl "wing_left" [model_new_instance $wing] #; instantiate a wing and add it to the jet, left
model_add_element $mdl "wing_right" [model_new_instance $wing] #; instantiate a wing and add it to the jet, right
set sim [simulator_load "simplified_linear_air_flow" "/mnt/proj/777/worlds.xml"] #; load some linear simulator with predefined parameters from an xml file
simulator_add_object [model_new_instance $mdl] ;# instantiate a jet in the simulator
simulator_set_param $sim "altitude" 12000
simulator_set_param $sim "temperature" -54
simulator_set_output "/tmp/sim.dmp"
simulator_run "10 sec"
exit
答案 2 :(得分:1)
一般来说,我总是根据我编写的代码生成器找到代码生成器最好避免使用,因为它们往往是繁琐,难以实现和人为限制。实际上,代码生成器将超越原始意图。
明智的做法是,除非您真的想要设计和实施专门针对您的领域量身定制的新语言,否则您最好使用预先存在的技术找到解决方案。
我知道似乎代码生成器会让事情变得更容易,节省打字并且通常很棒,但实际上这太乐观了。
如果我在C ++中这样做,我首先会花一些时间设计一个连贯的对象模型,可能使用JSBSim方法将模型包含在XML文件中。
因此,按照您的编辑,我会说最好花时间建立一个干净的模型,一些很好的文档示例和一些培训。
以下是用于说明的粗略原型模型。这可能与你的编辑无关,但由于我花了一段时间把它放在一起,我想我也可以发布它。
#include <string>
using namespace std;
//
// Fundamental element of simulation - an ExecModule is a concise unit of simulation work. It will
// be called by the main real time executive at the frequency specified by the getExecRate() method.
// Communication between modules is either via datapool, or by using BusMessages.
class ExecModule
{
public:
virtual bool initialise(long time_ms) = 0;
virtual long run(long ms) = 0;
virtual long getExecRate() = 0;
virtual string getModuleDescription() = 0;
}
class GeoCoordinate
{
public:
GeoCoordinate(double lat, double lon, double alt);
};
class Model
{
public:
virtual void DifferentialEquations() = 0;
virtual void AlgebraicEquations() = 0;
};
class State
{
public:
Value Prime();
Prime(Value &v);
State operator *(State c);
}
class AircraftModel : public ExecModule, public Model
{
private:
State x1, x2;
State x3;
InputDouble u;
InputBool flag1, flag2;
AlgebraicDouble x1x2;
Model tw1, tw2; // engine
Model gear;
Model isa;
TrimRoutine HorizontalFight;
TrimRoutine OnGround, General;
ConstantDouble c1, c2;
ConstantInt : ci1;
public:
AircraftModel()
{
}
public:
virtual void DifferentialEquations()
{
x1.Prime(2.0*x2);
x2.Prime(x1x2);
}
virtual void AlgebraicEquations()
{
x1x2 = x1 * x2 + x1.Prime();
}
public: // Required for ExecModule
string getModuleDescription()
{
return "Aircraft Model";
}
long getExecRate()
{
return 33L;//ms (30hz)
}
long run(long ms)
{
return 0L;
}
bool initialise(long time_ms)
{
// called by the Exec (in sequence) when initialisation is required.
}
};
class SimLoad
{
public:
// exec modules to load
class Model *aircraft_model;
class Model *engine_model;
class Model *aerodynamics_model;
class GPSSimulator *gps;
class FeaturesDataProvider *runways;
class ArincDB *arincDB;
class ExecSystem *execSystem;
SimLoad()
{
engine_model = new EngineModel();
aerodynamics_model = new AeroDynamicsModel();
aircraft_model = new AircraftModel();
arincDB = new ArincDB();
gps = new GPSSimulator();
// ensure that the simulated systems are loaded in the correct
// sequence. Notice that the exec system provides two schedulers which
// we select manually to allow for threading. Each thread within the exec is
// synchronised at the start of each frame (iteration) however within each frame
// each thread is free running so care needs to be taken when scheduling dependant
// modules across different threads.
execSystem.scheduler.addModule(engine_model);
execSystem.scheduler.addModule(aerodynamics_model);
execSystem.scheduler.addModule(aircraft_model);
execSystem.scheduler1.addModule(gps);
runways = new ArincRunwayProvider(arincDB);
execSystem.start(); //
}
}