2010-06-06 61 views
4

我正在评估为我的飞行模拟项目使用代码生成。更具体地说,要求允许“平均工程师”(我自己不是犯罪)来定义描述动态系统的微分方程,而不是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语言的灵活性只定义定义的某些部分是重要的,实现的模型类。这样一位工程师就可以从上述示例描述语言中提供模型,维护工程师将添加所有代码以从文件中读取参数,启动/停止/暂停仿真的执行以及具体对象如何实例化。

我的第一个任务是从描述符文件中生成两个文件:一个包含声明的.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:接受的答案

我接受理查德·哈里森由于与自定义代码发电机分享他exeprience给出了答案。我也会考虑有关脚本界面的提示,尽管我不想把时间作为我论文的一部分来实现。尽管如此,这些指标可能有助于说服我的同事们考虑更为连贯一致的阶级政体。非常感谢!

+0

表达式解析器肯定会使这个可行。但它有多慢?你难道不会通过这些方程数百万次? – 2010-06-06 12:40:17

+0

使用Modelica定义它,如果您在便宜的滑板公司使用免费编译器,并且如果您有现有的库,则使用它的外部函数接口。 – 2010-06-06 13:46:24

+0

@Hans:好的,我想通过它精确地一次性:先前编译生成的代码。 – Arne 2010-06-07 07:09:14

回答

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(); // 
    } 
} 
+0

+1为您提供示例。感谢您对代码生成器可能发展的评论。 – Arne 2010-06-07 08:00:35

+0

我不得不不同意。代码生成器是工具。像任何工具一样,代码生成器在正确使用/使用时会创造奇迹。但是,要做到这一点,这是另一回事:) – 2010-06-07 10:35:08

2

这种方法的问题是数学语法中的任何错误只有在代码由C++编译器实际编译时才会被检测到。在这一点上,很难提供与规范语言中错误的特定行相关的错误消息。因此,除非您的规范语言处理器也对规范进行了语法和语义检查(即至少是规范语言的部分编译器),否则我不会采用这种方法。

+0

你有一个点。语法错误是可以预料到的,只会被编译器捕获。但是,如果您直接将表达式直接输入到C代码中,情况也是如此。鉴于一个好的解析器可能会事先发现最明显的错误,甚至可能会添加一些语法糖,例如所谓的^运算符来指数。 – Arne 2010-06-06 10:34:34

+0

+1提到语法错误的问题 – Arne 2010-06-06 10:35:15

+1

@Arne最好不要添加 - 如果用户真的想要异或怎么办?实际上,C++没有一个正确的原因--C++委员会检查了一大堆FORTRAN代码(FORTRAN确实有这样的操作),并发现它实际上很少使用。这在Stroustrup的D&E书中有描述。 – 2010-06-06 10:42:07

1

我会走另一条路。在C/C++中实现所有基本类型,组合功能,遍历,模拟和流控制功能,并添加丰富的交互式shell。

这可以使用SWIG以最小的努力完成。它为应用程序添加了脚本接口,并支持大量脚本语言。

拥有带丰富shell接口的模拟器,具有领域知识的工程师(不是软件工程师)可以轻松地创建用于仿真/建模的原型脚本,调试和调整这些脚本,然后,如果性能不够,瓶颈代码可以轻松地由软件所有者从脚本移植到本地C/C++代码。

该方法用于大多数EDA系统(Cadence,Synopsys等),必须在模型中模拟> 10e9单位的系统,并且已被证明是CAD软件的最佳解决方案。实际上,几乎从不需要将脚本重写为本地语言,因为大部分时间都花在微分方程系统求解器上,用本地语言实现。

ADDITION: 您可以查看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 
+0

请参阅编辑我的问题。我仍然对你提到的这些脚本的一小部分样本感兴趣,以便更好地了解整个问题 - 以防你手边有片段。 – Arne 2010-06-06 11:56:59

+0

@Arne - 请参阅我的除了答案 – bobah 2010-06-06 13:59:27

+0

感谢您的示例和教程指针。我仍然不确定我是否可以实现一个完整的脚本界面,因为它本质上是我的毕业论文,也有一些“科学”的东西。 尽管如此,我感谢您的意见,并会考虑它。 – Arne 2010-06-07 08:03:39