开发者

How to best integrate generated code

开发者 https://www.devze.com 2023-01-02 16:46 出处:网络
I am evaluating the use of code generation for my flight simulation project. More specifically there is a requirement to allow \"the average engineer\" (no offence I am one myself) to define the diffe

I am evaluating the use of code generation for my flight simulation project. More specifically there is a requirement to allow "the average engineer" (no offence I am one myself) to define the differential equations that describe the dynamic system in a more natural syntax than C++ provides. The idea is to devise a abstract descriptor language that can be easily understood and edited to generate C++ code from. This descriptor is supplied by the modelling engineer and used by the ones implementing and maintaining the simulation environment to generate code.

I've got something like this in mind:

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

It is important to retain the flexibility of the C language thus the descriptor language is meant to only define certain parts of the definition and implementation of the model class. This way one engineer provides the model in from of the descriptor language as exemplified above and the maintenance engineer will add all the code to read parameters from files, start/stop/pause the execution of the simulation and how a concrete object gets instantiated.

My first though is to either generate two files from the descriptor file: one .h file containing declarations and one .cpp file containing the implementation of certain functions. These then need to be #included at appropriate places

[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"

Any thoughts or suggestions?

EDIT1:

I would like to clarify that there exists a framework to express the differential equations and the (real-time) simulation of a dynamic sytem. The problem is that most of the engineers with domain knowledge are very hesitant regarding the use of C++. Indeed we would like to provide a easer way for them to contribute their part (the mathematical formulation) while retaining the flexibility of C++.

There is of course the possibility to use the MEX compiler to generate C-code from MATLAB/Simulink models but we would like to avoid the associated license fees.

Refactorin开发者_JAVA技巧g the whole thing to use a general scripting language is probably out of scope in terms of workforce available. Furthermore this would -- as far as I can grasp it now and on the fly -- require the engineers to learn a scripting language, too. I am not yet convinced that this would be easier than the syntax exemplified above.

EDIT2: Accepted answer

I accept the answer given by Richard Harrison due to sharing his exeprience with custom code-generators. I will also consider the hints regarding scripting interfaces although I am afraind I will not find the time to implement all this as part of my thesis. Still these pointers might help to convince my coworkers to consider a more coherent class heriarchy. Thanks a lot!


The problem with this approach is that any errors in the mathematical syntax will only be detected when the code is actually compiled by the C++ compiler. At that point it is very difficult to give good error messages that relate back to the specific lines in error in the specification language. So unless your specification language processor also performs syntax and semantic checks on the specification (i.e. it is at least a partial compiler for the specification language), I would not take this approach.


Generally I've always found based on the code generators that I've written that code generators are best avoided as they tend to be fiddly, hard to implement and an artificial limitation. In effect a code generator will grow beyond original intentions.

It is wise to realise that unless you really want to design and implement a new language specifically tailored to your domain you are best off finding a solution using pre-existing technologies.

I know it seems that a code generator will make things easier, save typing and generally be wonderful, but realistically this is too optimistic.

If I were doing this in C++ I would firstly spend some time designing a coherent object model, possibly using the approach taken with JSBSim of having the model contained within an XML file.

So following your edit I would say that it is probably best to invest the time in building a clean model, some good documented examples and some training.

The following is a rough prototype model for illustration. It may not be relevant given your edit, but as I've just spent a while putting it together I figured I might as well post it.

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


I would go another way. Implement all primitive types, composition functionality, traversal, simulation, and flow control functionality in C/C++ and add a rich interactive shell.

This can be done with minimum effort using SWIG. It adds a scripting interface to the application and lots of scripting languages are supported.

Having a simulator with rich shell interface the engineers (not software engineers) with domain knowledge can easily create prototype scripts for simulation/modelling, debug and tune these scripts, and then, if the performance is not enough, the bottleneck code can be easily ported by the software owner from the scripts to the native C/C++ code.

This approach is used in the most EDA systems (Cadence, Synopsys, etc.) having to simulate the systems with >10e9 units in the model and has proven to be the best solution for the CAD software. In fact, it is almost never required to rewrite scripts into the native language, because the most time is spent in differential equations systems solvers, implemented in native languages.

ADDITION: You can have a look at the tutorial explaining how to enable a scripting interface in C++ program. I would choose TCL as it is a very simple yet sufficient scripting language, all commands can fit on one page. There should not be any problems teaching engineers and you can always document only a small subset of the functionality, to mimic your original syntax example.

Your simulation script would look as below

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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号