Writing Dynamic DLL's

Navigation:  Language Reference > Functions > External > dlldynamic >

Writing Dynamic DLL's

Previous pageReturn to chapter overviewNext page

Almost every compiler that can build Windows applications, can also build dll-files containing dll-functions. For the Visual C++ compiler an example will be given how to build the dynamic dll's (i.e. dll functions that may contain internal model states) in combination with 20-sim.

 

The functions in the DLL are called in a specific sequence. For communication with the functions and the Simulator Kernel information is passed in a structure. This structure looks like this:

 

struct SimulatorSFunctionStruct

{

 double versionNumber;

 int nrInputs;

 int nrOutputs;

 int nrIndepStates;

 int nrDepStates;

 int nrAlgLoops;

 double simulationStartTime;

 double simulationFinishTime;

 double simulationCurrentTime;

 BOOL major;

 BOOL initialOutputCalculation;

};

 

1. Initialize

At the start of the simulation the function

int Initialize()

is called. When this function is not present it is not called. Any initializations of data structures can be performed here.

 

2. SFunctionInit

During initialization of the Simulator Kernel the function

int SFunctionInit(SimulatorSFunctionStruct *s)

is called. Return value is 0 means error. every other value succes Argument is a pointer to the simstructure. On initialization the following fields should be filed in:

nrIndepStates

nrDepStates

nrAlgLoop

The following fields already have valid values:

 

simulationStartTime

giving the start time of the simulation

 

simulationFinishTime

giving the finish time of the simulation

 

simulationCurrentTime

giving the current time (actually the start time at the moment of initialization)

 

3. Initial values for the states

int SFunctionGetInitialStates(double *initialIndepStates,

double *initialDepRates,

double *initialAlgloopIn,

SimulatorSFunctionStruct *simStruct);

 

Return value is 0 means error. every other value succes. The initial value for the independent states, dependent rates and algebraic loop variables can be specified by the DLL in this function. It is just called before the initial output calculation function in step 3. If all the initial values are zero, nothing has to be specified.

 

4. Initial Output Calculation

It is possible that the DLL-function can give an initial output. A separate function is called so that the DLL can calculate it's initial output values. The boolean initialOutputCalculation in the simulatorSFunction structure is used. Just the sFunction is called. as in point 5.

 

5. SFunction calling

Here all the fields of the SimulatorSFunctionStruct are input for the function. The inputArray, stateArray, outputArray and rateArray are always given as arguments of the function. Dependent on the number of dependent states and algebraic loop variables more arguments can be given as shown in the functions below (sFunctionName is the name defined by the parameter name specified by the user):

 

Return value is 0 means error. every other value succes

 

case: no dependent states, no algebraic loop variables

int sFunctionName(double *inputArray,

 double *stateArray,

 double *outputArray,

 double *rateArray,

 SimulatorSFunctionStruct *simStruct);

 

case: dependent states, no algebraic loop variables

int sFunctionName(double *inputArray,

 double *stateArray,

 double *dependentRateArray,

 double *outputArray,

 double *rateArray,

 double *dependentStateArray,

 SimulatorSFunctionStruct *simStruct);

 

case: no dependent states, algebraic loop variables

int sFunctionName(double *inputArray,

 double *stateArray,

 double *algLoopInArray,

 double *outputArray,

 double *rateArray,

 double *algLoopOutrray,

 SimulatorSFunctionStruct *simStruct);

 

case: dependent states, algebraic loop variables

int sFunctionName(double *inputArray,

 double *stateArray,

 double *dependentRateArray,

 double *algLoopInArray,

 double *outputArray,

 double *rateArray,

 double *dependentStateArray,

 double *algLoopOutrray,

 SimulatorSFunctionStruct *simStruct);

 

the boolean major in the SimulatorSFunctionStruct determines whether the evaluation of the model is done at the time output is generated (major == TRUE ) or that the model is evaluated because of determining model characteristics. For example Runge-Kutta4 integration method uses three minor steps before taking a major step where output is generated. Higher order methods can have different number of minor steps before a major step is taken.

 

6. Termination

At the end of the simulation the function:

int Terminate()

is called. When this function is not present it is not called. Any terminations of data structures can be performed here.

 

Framework for a Visual C++ dll-file implementation.

 

#include <windows.h>

#define DllExport __declspec( dllexport )

extern "C"

{

 DllExport int dllfunction(double *inarr, int inputs, double *outarr, int outputs, int major)

 {

         ... // function body

         return 0; // return successful

 }

 DllExport int Initialize()

 {

         ... // do some initializations here.

         return 0; // Indicate that the dll was initialized successfully.

 }

 DllExport int Terminate()

 {

         ... // do some cleaning here

         return 0; // Indicate that the dll was terminated successfully.

 }

}

 

Framework for a Borland C++ dll-file implementation.

 

#include <windows.h>

extern "C"

{

 int _export dllfunction(double *inarr, int inputs, double *outarr, int outputs, int major)

 {

         ... // function body

         return 0; // return successful

 }

 int _export Initialize()

 {

         ... // do some initializations here.

         return 0; // Indicate that the dll was initialized successfully.

 }

 int _export Terminate()

 {

         ... // do some cleaning here

         return 0; // Indicate that the dll was terminated successfully.

 }

 

}

// Every dll has an entry point LibMain || DllEntryPoint

// and an exit point WEP.

BOOL WINAPI DllEntryPoint(HINSTANCE hinstDll, DWORD fdwRreason, LPVOID plvReserved)

{

 if (fdwRreason == DLL_PROCESS_ATTACH)

 {

         ... // do some initializations here.

         return 1; // Indicate that the dll was initialized successfully.

 }

 if (fdwRreason == DLL_PROCESS_DETACH)

 {

         ... // do some cleaning here

         return 1; // Indicate that the dll was initialized successfully.

 }

 return 0;

}

 

Working example

Here is a complete working example of how to use a dll-function from within the simulator. This code will compile with Visual C++. See the Borland C++ framework how to write this code in Borland C++.

 

#include <windows.h>

#include "SimulatorSFunctionStruct.h"

 

/*******************************************************************************

* in this source file we are gonna describe a linear system which is defined by

* the following transfer function description:

 

 34

Y = ---------------- * U

 s^2 + 6s + 34

 

or A, B, C, D system:

 

A = [ 0, -3.4;

 10, -6];

B = [ -3.4;

 0];

C = [0, -1];

D = 0

which has two poles on (-3 + 5i) and (-3 -5i)

 

 steady state = 1

 

 ******************************************************************************/

 

#define DllExport __declspec( dllexport )

 

extern "C"

{

 

 // called at begin of the simulation run

 DllExport int Initialize()

 {

         // you can perform your own initialization here.

 

         // success

         return 0;

 }

 

 // called at end of the simulation run

 DllExport int Terminate()

 {

         // do some cleaning here

 

         // success

         return 0;

 }

 

 DllExport int SFunctionInit(SimulatorSFunctionStruct *s)

 {

         // tell our caller what kind of dll we are

         s->nrIndepStates = 2;

         s->nrDepStates = 0;

         s->nrAlgLoops = 0;

 

         // dubious information, since 20-sim itself does not check and need this info

         s->nrInputs = 1;

         s->nrOutputs = 1;

 

         // return 1, which means TRUE

         return 1;

 }

 

 DllExport int SFunctionGetInitialStates(double *x0, double *xd0, double *xa0, SimulatorSFunctionStruct *s)

 {

         // fill in the x0 array here. Since we specified no Dependent states, and No algebraic loop variables

         // the xd0 and xa0 may not be used.

 

         // initial value is zero.

         x0[0] = 0;

         x0[1] = 0;

 

         // return 1, which means TRUE

         return 1;

 }

 

 DllExport int SFunctionCalculate(double *u, double *x, double *y, double *dx, SimulatorSFunctionStruct *s)

 {

         // we could check the SimulatorSFunctionStruct here if we are in an initialization state and/or we are

         // in a major integration step.

#if 0

         if (s->initialialOutputCalculation)

                 ; // do something

 

                 // possibly do some explicit action when we are in a major step.

         if (s->major == TRUE)

                 ; // do something

#endif

         dx[0] = -3.4 * x[1] - 3.4 * u[0];

         dx[1] = 10 * x[0] - 6 * x[1];

 

 

         y[0] = -x[1];

 

         // return 1, which means TRUE

         return 1;

 }

 

}// extern "C"

BOOL APIENTRY DllMain(HANDLE hModule,

 DWORD ul_reason_for_call,

 LPVOID lpReserved

)

{

 return TRUE;

}

 

Use within 20-sim

Suppose the dll has been created as "demoDynamicDll.dll". With the following code this model can be tested:

 

parameters

 string dllName = 'demoDynamicDll.dll';

 string functionName = 'SFunctionCalculate';

equations

 output = dlldynamic (dllName, functionName, input);

 

Note that the general function "dlldynamic" is used. The arguments of this function, dllName and functionName, are parameters which are used to denote the dll that should be used and the function of that dll that should be called. You can load this model from the Demonstration Models Library:

1.Open the Editor.
2.From the demo library open the model DllFunction.emx (choose File and Open)
3.Start the simulator (Model menu and Start Simulator).
4.Start a simulation run (select Run from the Simulation menu).