State machine technology, Generator for C and C++

State machine technology, Generator for C and C++

Inhalt


Topic:.stateM_en.

Statemachines are well known both for hardware design (FPGA, hardware logic) and in software. Especially the UML defines statecharts as one of diagram kinds. Statecharts in UML support special features such as parallel states and nested states with defined rules of executing.

In cohesion with statemachines or statecharts in software the Event driven execution is used often. Events are data objects which contains information. Events can be stored in queues and be executed in another thread. In this kind it is a helpfull mechanism for thread interactions.

Both, statemachines respectively statecharts and events are present for software development if an UML-tool is used. For ordinary development of software in statement-oriented languages such as C, C++ or Java, statemachines and events are not used as medium of expressibility in the programming language often. Instead quests and set of boolean or enum-Variables are used to save the state of software. That is because:

Thinking in events and state machines is another approach than procedurale programming. It may be adequate to use that technologies of software. But if that technology is bounded to special tools for UML, it is not used ubiquitously.

This article describes a framework for programming in states and events for Java and C/C++ language for normal line sources without usage of UML-tools but UML-conform. Some aspects of software technology are discussed.


ERROR: not found ---/root//topics:topic[@ident='stateM_en']/topics:topic[@ident='stmC']/topics:topic[@ident='howC']---



1 State machines in C and C++ - adequate approach

Topic:.stateM_en.stmC.

See article StateMGen

State machines are used in C or C++ often in a simple kind. Therefore they are programmed usually manually with switch - case, setting of the state variable immediately and without specific entry and exit actions. Using of an enum definition of the states is a good idea:

switch(stateVariable) {
  case EnumStateX: {
    if (condition) {
      action();
      stateVariable = EnumStateY;
    }
  } break;
  ....
} //switch

If the requirements for state processing are more complex, this schema is limited. How to write nested states? How to write parallel states? Unique programming of onEntry and onExit? Event handling?

It is the same problem like in Java: The written code should be short and pitty, but the necessities for execution need a verbose execution code. The solution in Java is (see $chapter): using reflection to generate the necesarry data, use it in prefabricated methods of the base (super) classes of the states. This principle does not work in C language. There are not reflection, inheritance (in C++) should not be used.

Therefore another approach is used: Code generation. Therefore a ZBNF parser is used to parse and a JZcmd is used to generate the C file. Both tools are script controlled. The user can change the scripts without study the complex programming of the generater, to adapt to its requirements to the execution code.

Source                               Internal     Prepared                          2. Source
State machine----> ZBNF parser ----> Java data ---Java data----> JZcmdExecuter ---> C-program
(any language)          ^            image                            ^             generated
==============          |                                             |             =========
                   ZBNF syntax                                   JZcmdScript
                   script                                             ^
                   ==========                                         |
                                                                      |
                                                 JZcmd   -------> JZcmd using
                                                 generation       ZBNF parser
                                                 script
                                                 ==========

The source language for the state machine's code can be any language. It is determined and change-able by the ZBNF syntax script and the JZcmd generation scripts.


2 Patten for StateMachine's source code in C

Topic:.stateM_en.stmC.patternCpp.

The source code in C is written in this articel with a background color in yellow:

...The source code...

whereby the generated code is written with a light blue background:

...The generated code in C...

The user should write the definition of the State machine in a header file. The example source file is examples_Zmake/StateMGen/src/ExampleSimpleData.states.h.html. This is a full example for one state of the statemachine in the chapter Parallel and nesting states ....

#ifndef __ExampleSimpleData_states_h__
#define __ExampleSimpleData_states_h__

/* stateMcHgen-Syntax: firstly that header is included, which will be generated then. */
#include "../result/exampleSimpleData_genState.h"

/* stateMcHgen-Syntax: secondly this header is named by define, to know for include. */
 #define SrcHeader_StateMgen "ExampleSimpleData.states.h"

That are the first lines of the header file. It starts with the guard for header files with any definition. The first include should name the generated header file. It is included here, after translation of course. Some generated definitions are used. Secondly this header itself is named, it is used for include in the generated C-File. A line

#include "ExampleSimpleData.states.h"

is produced in the generated C-file with them.

The next lines can include some other files additionally which are necessary especially to access the states data inside the user struct. Therefore the Headerfile which defines that user struct is included here. It is not used for code generation but for C translation of the generated code too.

/* stateMcHgen-Syntax: Includes the header file for the compilation unit, where the class is defined which contains the generated State structure. */
#include "ExampleSimpleData.h"

The some definitions should follow. That definitions are used to generate the correct code for some things, see description in the example source code:

 /* stateMcHgen-Syntax: Definition of the type of struct for the users data which are associated to this state machine.
  * This Definition is used as suffix for function names to get unique identifier. */
 #define UserDataType_StateMgen ExampleSimpleData
 /* stateMcHgen-Syntax: Definition of the type of struct for the states. It is used as struct type name for generation. */
 #define TopStateType_StateMgen StateMachine_ExampleSimpleData
 /* stateMcHgen-Syntax: Definition of the access to the state data inside the users data.
  * It should be matching to the TransFn args. In this example it uses the 'thiz' pointer from the Transfn args.
  * Note: write it without spaces. */
 #define StateInstance_StateMgen thiz->state

The TransFn is a function type which is used for all transition codes for the state machine.

/* stateMcHgen-Syntax: Prototype especially for all transition execution routines from any state to a given destination state.
 * The same arguments but slightly a different return value are used for all generated functions for entry, exit, doTrans, currentState too.
 * Coincidently, the same argument names should be used for the exit and entry routines.
 */
typedef int TransFn(struct ExampleSimpleData_t* thiz, int event);

Now the definitions of the states follow:

/* stateMcHgen-Syntax:  Any state have to be defined with its transitions to any other state, maybe with an parent state.
 * If the parent state is not given here, it is a state at top level.
*/
/**Write a comment for documentation for the state here.
 */
typedef struct Off_State_t {
  /**The internal state data, the identifier after underline is the state number*/
  StateBaseGen Top;
  /**A transition is defined as TransFn pointer. The name of the transition determines the destination state(s).
   * The names are the simple state type names independent of their nested structure.
   * If it is a fork transition, the state should be separated by underliner _ //TODO remove: Special: history on end: Go to the history pseudo state.
   * Write a comment here for any transition:
   * If on_cont is given, the state Work is entered and the work is continued at the history state. */
  TransFn* workHistory;
  /**If on_ready is given, the statemachine activates Ready, independent of the history state. */
  TransFn* Ready;
} Off_State;

The Definition of any state is created as const data in the generated C-code. It should contain a base struct StateBaseGen. This is a generated typedef in the local scope inside the generated header:

This element in the state structure has a meaning for code generation: The name describes the parent or enclosing state. For the top level, or for a simple state machine without nested states, the name of the parent should be Top like shown here.

After this StateBaseGen all transitions should follow with any identifier name. Only a History Transition should end with

The transitions are used in the manual written transition routine.

In the generated header, this type StateBaseGen has the following form:

/**This structure is the base structure of any state in this context for the const STATE_StateConst definition.
 * It is special, therefore code-generated, because the TransRnGen and the ExitRnGen hava a special signature in the users context.
 * The assembling of the data is the same for all code-generated state machines of this tool StateMcHgen.
 */
typedef struct StateBaseGen_t {
  void const* signature;
  int id;
  int ixCompositeState;
  int ixOwnState;
  ARRAYCONST_SimpleC(struct StateBaseGen_t const*) statePath;
  /**Kind of the states, see bit definition in Fwc/fw_StateMachine.h. */
  int kind;
  StateCurrentRnGen* stateCurrent;  //to get the current state of a composite.
  TransRnGen* checkTrans;           //Function pointer, routine pointer
  EntryRnGen* entry;                //Function pointer, routine pointer
  ExitRnGen* exit;                  //Function pointer, routine pointer
  ExitParallelRnGen* exitParallel;  //to exit a Composite or Parallel state.
} StateBaseGen;

After the definition of any state either a forward declaration of the entry-, exit- and trans routine should be written, or the routine is written as inline or static, like usual in header files:

//stateMcHgen-Syntax: If a prototype or definition for entry_-, exit_-, in_- and checkTrans-_ is given,
//that routines are called by the generated state machines code.
//* That routines are arranged after the state's struct definition. Therewith they are recognized as associated to the state.
//* The names of the routine should start with 'entry_', 'exit_', 'checkTrans_'. The rest of the name is arbitrariness.
//It should be proper for the users association.
//* The routines should be defined by normal C-Programming in the compilation unit.
//* If they are written as inline, the keyword 'INLINE_Fwc' may be used instead 'inline' or 'static'.
//It's defined in os_types_def.h proper to the platform.
//* The argument names have to be the same like used in the 'TransFn' definition because that names are used for calling
//in the code generation of the doTrans- routine.
//
/**An comment is possible here.*/
INLINE_Fwc void entry_Off(struct ExampleSimpleData_t* thiz) {
  thiz->work = false;  //
}  //stateMcHgen-Syntax: If the routine is defined with a body, only the last '}' should be written on start of line.
//Therewith it is recognized by the parser. The end is '\n}' or '\r}'.
/**An comment is possible here.*/
INLINE_Fwc void exit_Off(struct ExampleSimpleData_t* thiz) {
  thiz->off = false;  //prevent immeditately switch to off if this condition is set already and erroneously.
}

The name of the methods after entry..., exit... and checkTrans... can be given with any identifier. The association to the correct state is given because the methods are written after the state structure definition. The names of the arguments should be the same as in the TransFn function type definition. The checkTrans...-Method should have an additional argument with is the yet defined state type. It is used to select the transitions for this state in the body of the routine. The name of any transAction_ routine should match to the name of the TransFn!

//stateMcHgen-Syntax: The checkTrans routine for any state should be declared in this form.
//The name of the routine should start with 'checkTrans...', the rest is no matter.
//The name of the state is the Type of the last parameter named 'state'. It should be matched to the state struct definition above.
//The code of the checkTrans routine is invoked by the genarated code. It is not regarded for code generation.
//
int checkTrans_Off(struct ExampleSimpleData_t* thiz, int event, Off_State const* state);
//stateMcHgen-Syntax: If a 'transAction_...' is given, it is invoked in the correct order after exit the states.
//The name of the trans action should match to the name of the transition in the form <stateName>_<transitionName>.
//
void transAction_Work(struct ExampleSimpleData_t* thiz);

It follows the next state etc.

A State is a 'StateCompositeFlat' or a 'StateComposite' if another state refers it:

typedef struct Ready_State_t{
  StateBaseGen  Work;
  /**If start is given, the state running is entered, and as parallel state the RemainOn.
   */
  TransFn* Running_RemainOn;
} Ready_State;

It is a state as nested state of Work therefore Work is a 'StateComposite (Flat)'. Work is a 'StateComposite', not 'Flat' because it contains an history-pseudostate.

The difference between 'StateComposite' and '...Flat' is: A 'StateComposite' has a state variable. A 'StateCompositeFlat' is an enclosing state, contains an inner state Machine, but the inner state is controlled by the superior 'StateComposite' or the top state. The reason for a 'StateCompositeFlat': There are transitions which are valid for all inner states. The other reason: extra entry and exit routines.

A StateParallel is designated with the parallelBase_... identifier, following with its enclosing state:

typedef struct Active_t
{ StateBaseGen parallelBase_Work;
} Active;

A state is a composite state if any state refers to it as StateBaseGen. The state Work in this example contains the Active as a substate. Active is a state which contains more as one parallel inner state machine.

The Active1 in this example is one of the parallel states in the StateParallel Active because it refers to it.

typedef struct {

 StateBaseGen Active;

} Active1;

Because the Running_State refers to Active1, the Active1 is a 'StateCompositeFlat'. Because Active1 has a 'StateParallel' as its base, it is a 'StateComposite', not flat:

typedef struct {

 StateBaseGen Active1;
 TransFn* FinitState;
 ...

} Running_State;

That's all in the header file:

#endif //__ExampleSimpleData_states_h__

The checkTrans...() and the entry- and exit routines which are forward declared here are written in any C-source. Usual it is the C-source-file associated to the UserDataType_.... It is not used for generation. The routines are invoked from the generated code. For debugging it is possible to set breakpoints there immediately.

In a checkTrans... routine the TransFn* ... form the State definition are used:

int checkTrans_Work(ExampleSimpleData* thiz, int ev, Work const* state) {
  if(ev == kOff_Event_ExampleSimple) {
    thiz->isWorking = false;
    return state->Off(thiz, ev);
  } else {
    return 0;
  }
}

This is the implementation (definition) of the checkTrans_Work(...) declared in the translated header file.

The transition to Off contains a transition code. It is executed before the exit(...) routines are invoked. That is not exactly UML-conform but usual the simplest and non-problematic way.

The state definition Work is given as last argument. Therefore in the body the transitions of Work are able to access. The transition routine referred as Function pointer in C are generated and contains all necessary exit, entry and set of state variables. That is not necessary to program manually.

TODO timeout.


3 Translation of the state machine's source, generation of code

Topic:.stateM_en.stmC.genCtrl.

The translation of the header file of the state machine to the C state execution code is done by a Java program:

java org.vishia.stateMGen.StateMGen -i:src/exampleSimple.state.h -s:../../zbnfjax/zbnf/StateMcH.zbnf
-d:result/data_Statem.txt -scriptcheck:result/data_script.txt -c:../../zbnfjax/zmake/States.genDocu.jzgen
-y:result/exampleSimple.topic -c:../../zbnfjax/zmake/stateMcHgenC.jzgen -y:result/exampleSimpleData_genState.c
 -c:../../zbnfjax/zmake/stateMcHgenH.jzgen -y:result/exampleSimpleData_genState.h --rlevel:333 --report:result/log.txt

That command with some arguments controls the generation. All input- and output files are given by the command line arguments. More simple is, using a Zmake script. It is a batch file for windows or a shell script:

REM: Note for Windows: use %0 to get the path of this batch, the same file is the input file of jzcmd.
jzcmd %0
exit /B
==JZcmd==
currdir = scriptdir;
include ../../zbnfjax/zmake/stateMcHgen.jzcmd;
main(){
  zmake "result/exampleSimpleData_genState.*" := stateMcHgen(src="src/exampleSimpleData.states.h");
}

The included script zbnfjax/zmake/stateMcHgen.jzcmd.html organize the java invocation with the given arguments.

The Java program parses the given source code in C++, composes some coherences between the data and generates the output files. The parsing of the source is script-controlled by the ZBNF parser. The syntax can be changed, another input form (language) can be used instead C++ only with changing the syntax file. The composition of coherences is widely the same like the algorithm for java statemachines, see How does it work - missing data via reflection. The generation of the output is controlled by a JZcmd script. In this script some adaptions can be done to influence the generated code. That is explained in an extra document StateMGen because it is more for system engeneers (administrators of a development process) as for the user programmer which will use state machines.


4 Parsing the Statemachine's source code, C++, alternative syntax, storing the parse result

Topic:.StateMGen_en.parse.

Last changed: 2015-11-01

The generation of state machine code is conceived for programming state machines in C or C++ with a conventional Integrated Development Environment (IDE) such as Microsoft Visual Studio or Eclipse CDT. It should not presumed that a special tool for state machines is used. he IDE "Microsoft Visual Studio" Version 6.0. The project file may be able to run in a higher version of this tool too or may be converted to another IDE. See examples_Zmake-readme:StateMGen.

The C++ syntax allows compiling the code only for syntax test in the given IDE. With that code no execution machine code is gotten because the source code is incomplete for state machine execution. But the correctness of the source can be simple checked with the C++ compiler. That's important because the bodies for entry(), exit() and the transitions contains any C++ code which is not tested by the Zbnf parser.

The state machine's source code of that example, see examples_Zmake/StateMGen/src/ExampleSimple.state.cpp.html starts with:

#include "ExampleSimpleData.h"
#include <stdio.h>

//Name all arguments of a state trans method as static variable here.
//In generated code there will be given in the argument list.
static struct ExampleSimpleData_t* thiz;
static int event;

The ZBNF-syntax to parse it, see zbnfjax:zbnf/StateMCpp.zbnf, starts with:

 <?ZBNF-www.vishia.de version="1.0" encoding="iso-8859-1" ?>
 $xmlns:topics="http://www.vishia.de/2006/Topics".
 $xmlns:xhtml="http://www.w3.org/1999/xhtml".
 $inputEncodingKeyword="encoding".
 $comment=/*...*/.
 $endlineComment=//.

 ##Top level Syntax for the C++ statemachine source:
 ##
 StateMachine::=
 { #include <* \n?includeLine>
 }


 ##argument for each state subroutine are written as static variable declaration.
 ##Therewith they are seen if they are used as arguments in bodies of the state methods.
 { static <*;?statefnarg>;
 }

The script starts with some settings, see the ZBNF parser main description. The statemachine is defined with the top level syntax StateMachine::=.... That starts with the accepting of the #include ... lines. Each included file is stored in an element includeLine.

The next lines are stored as statefnarg. It is taken for code generation later.

You can see the parsers result for this example because it is possible to write an html file which shows the content of Java instances. That file for the given example is ZBNF/examples_Zmake/StateMGen/result.cmp/exampleSimple.state.dataCheck.html In the top level instance of type org.vishia.stateMGen.StateMGen$ZbnfResultData you find a List includeLines which contains the both include lines of the example, and you find there the List<String> statefnargs with the both arguments too.

The syntax continous with the some more toplevel necesities, and then

  ##All states defined after them, a zbnf component
  { <state> }
\} ;
\e.

That is the whole top level syntax. Any state as a C++ class is defined in the component:

state::=
[ /**<* |\.|*/?shortdescription><* |*/?description> */]

[public : |] class <$?@stateName>  [| : public | : protected  <*\{?> ]  ##after : some super states for access in C++
\{
{ int statenr_<* ;?stateNr> ;                ##variable defines the state number as part of that int variable. Mandatory.
| int parallel<?stateParallel> ;             ##if this variable exists it is a parallel state.
| public : class History \{ \} ; <?hasHistory>
| [/**<* |\.|*/?-shortdescription><* |*/?-description> */]
  [ void entry ( ) \{<action?+entry> \}      ##method for entry the state, obligatory, but only one time per state.
  | void exit ( ) \{<action?+exit> \}        ##method for entry the state, obligatory, but only one time per state.
  | void trans  <trans?+trans>               ##method describes a transition, more as one possible.
  | void join  <join?+trans>                 ##method describes
  | void inState ( ) \{<action?+instate> \}  ##method for action in state, obligatory, but only one time per state.
| <state>                                    ##sub state in a composite state.
  ] }
\} ;
.

Any state class at top level is designated as <state>. The method srcJava_Zbnf/org/vishia/stateMGen/StateMGen.ZbnfStateCompositeBase#add_state(org.vishia.stateMGen.StateMGen.ZbnfState) stores the states in the List<ZbnfState> subStates. Because of that you find the both top level states Off and Work in exactly that list in the super class of the toplevel instance, shown in the html data report file ZBNF/examples_Zmake/StateMGen/result.cmp/exampleSimple.state.dataCheck.html. The capability of storing parse results is that of the class srcJava_Zbnf/org/vishia/zbnf/ZbnfJavaOutput

The content of the states is parsed as the component state::=... in the ZBNF syntax. Because the method srcJava_Zbnf/org/vishia/stateMGen/StateMGen.ZbnfStateCompositeBase#new_state() returns in instance of srcJava_Zbnf/org/vishia/stateMGen/StateMGen.ZbnfState which is stored with add_state the content is stored in that class type. In the data_StateSrc.html the storing instance is reported by a hyperlink from the toplevel instance to the component's instances. Follow the link to Off and Work to visit that content!

Next, view to the content of that both top level states, see also the original file examples_Zmake/StateMGen/src/ExampleSimple.state.cpp.html:

 /**It is the off-state. This part contains a documentation of the state
  */
 class Off
 { int statenr_1;
   /**Comment for entry-action. Note: If nothing is specified on entry, you can remove the entry method too.*/
   void entry(){
     thiz->work = 0;
   }//.
   /**Comment for exit-action. Note: If nothing is specified on exit, you can remove the exit method too.*/
   void exit(){
   }//.
   /**Comment for this transition. */
   void trans(bool cond = thiz->on_ready){
     thiz->work = 1; switchTo(new Work::Ready());
   }
   /**Comment for the other transition. */
   void trans1(bool cond = thiz->on_cont){ switchTo(new Work::History); }

   /**The method which should be done in the state, if the statemachine is invoked cyclically. */
   void inState(){
     thiz->counter +=1;
   }//.
 };

That state src code is parsed with the ZBNF component state::=. The result is stored via srcJava_Zbnf/org/vishia/zbnf/ZbnfJavaOutput to the instance which is referred with Off. In the ZBNF/examples_Zmake/StateMGen/result.cmp/exampleSimple.state.dataCheck.html#obj-2_1 you can see which content is stored with them.

The state Work has sub states. Therefore the List subStates of this state is filled with the reference to the stored data of the sub state.

The principle is repeated for all states, entry actions etc. etc. The result is a Java data image of the state source code which will be processed firstly:


5 Preparation of the parsed data

Topic:.StateMGen_en.prep.

The preparation of the parsed data is necessary because the source code of the state does not contain all necessities to execute the state machine. Especially the correct order of exit- and entry from the current to the desitination state may be more complex and is not contained in the source. Only the code for the exit- and entry-actions itself are defined in the source. The preparation completes the dependencies between the states with the given information by the parser.

See the parsed data: ZBNF/examples_Zmake/StateMGen/result.cmp/exampleSimple.state.dataCheck.html

and then the prepared data: ZBNF/examples_Zmake/StateMGen/result.cmp/exampleSimple.state.dataCheck.dst.html

The next

lines of Java source code
...

shows the method docuSrcJavaPriv_Zbnf/org/vishia/stateMGen/StateMGen.html#prepareStateData(org.vishia.stateMGen.StateMGen.ZbnfResultData) which is invoked after the parsing and storing data in ZbnfResultData:

The preparation works in the same way as preparing state machines in Java. But the parsed data should be preprocessed. The preparation creates firstly an instance of docuSrcJavaPriv_Zbnf/org/vishia/stateMGen/StateMGen.GenStateMachine which refers the ZBNF-parsed data zbnfSrc. It takes the parsed result data from the instance of ZbnfResultData which is referred in genStm for generation usage:

 //This is a excerpt of the Java source
 //[[docuSrcJavaPriv_Zbnf/org/vishia/stateMGen/StateMGen.GenStateMachine#prepareStateData()|stateMGen/StateMGen.GenStateMachine#prepareStateData()]]
 void prepareStateData(ZbnfResultData zbnfSrc){
   //creates the instance for all prepared data:
   genStm = new GenStateMachine(zbnfSrc);

Then the stateTop which is created in the ctor of GenStateMachine is completeted:

   StateComposite stateTop = genStm.stateTop();
   stateTop.setAuxInfo(new GenStateInfo(null)); //instance for Code generation for the top state.

After them the root State and all states of the parsed result are added to internal lists:

   //gather all states and transitions in the parsed data and add it to the prepared data:
   genStm.rootStates.add(stateTop); //the stateTop is the first rootState.
   gatherStatesOfComposite(stateTop, stateTop, zbnfSrc);
   //
   gatherAllTransitions();

With this operations two things are made:

Last not least the prepare() is invoked, it is the same like for Java state machines:

   //invoke prepare, the same as for Java state machines.
   genStm.prepare();
 }
 ....
   void prepare() {
     topState.prepare();
   }
 ...
   public void prepare() {
     buildStatePathSubstates(null,0);  //for all states recursively
     //the transitions are added already. Don't invoke createTransitionListSubstate(0);
     prepareTransitionsSubstate(0);
   }

The routines buildStatePathSubstates(...) and prepareTransitionsSubstate(0) are invoked for preparing of a Java statemachine too in the constructor of docuSrcJava_vishiaBase/org/vishia/states/StateMachine#StateMachine(java.lang.String, org.vishia.event.EventTimerThread) respectively StateMachine(String name)

As the result the data inside the docuSrcJavaPriv_Zbnf/org/vishia/stateMGen/StateMGen.GenStateMachine are completed correctly to generate the state machine's code. You can see the data content of the class StateMGen.GenStateMachine and all its aggregated classes in the file


The most important capability of this preparation is the ascertainment of the pathes from the current ot the destination state for any transition. For example you see that prepared data in ZBNF/examples_Zmake/StateMGen/result.cmp/exampleSimple.state.dataCheck.dst.html#Trans_ShouldOff2 which is the transition from state ShouldOff to Off. To help getting that any state has its path as result of preparation, see element statePath in all instances of StateSimple.


6 Generation of the h- and c-files

Topic:.StateMGen_en.gen.

Visit the example source and generated files:

The jzcmd respectively jzgen script:


6.1 Documentation style

Topic:.StateMGen_en.gen..

This chapter shows details of


6.2 Generation script style and data basics

Topic:.StateMGen_en.gen..

The basic data are given with the invocation of the generation in Java. It is:

 JZcmdExecuter generator = new JZcmdExecuter(console);
 generator.setScriptVariable("sOutfile", 'S', fOut.getAbsolutePath(), true);
 generator.setScriptVariable("stm", 'O', genStm, true);
 try{
   JZcmd.execute(generator, fileScript, out, console.currdir(), true, fScriptCheck, console);

There are 2 script variables defined before the script was started:

Line feed rule:

The line feed is given usual as start of the line, not for its end. It is often better for the formulation in the script. The start of line is the more important thing often:

    <+>
:::://This is the start of the new line. <.+>

In this kind an expression

 if(...) {
   <+>enhances the line<.+>

And last a

    <+>
::::<.+>

produces the last line termination.

If a first line should be written without a previous line feed but the script show a new line it is written:

    <+><:s>
::::first line, or continue a previous line.
::::<.+>

The special symbol <:s> skips over all white spaces in the generation script inclusively the first indentation of a line.


6.3 The generated Headerfile

Topic:.StateMGen_en.gen..

The header file should start with the guard. The script zbnfjax/zmake/States.genH1.jzgen.html starts with

Filepath outfile = &sOutfile; ##sOutfile is defined in the java calling environment.

The first line defines a script variable from the given variable sOutfile which converts the text given absolute file path to an instance of srcJava_vishiaBase/org/vishia/cmd/JZcmdFilepath which allows separating the name, the extension etc. The sOutfile was defined in the java calling environment:

The next lines in the script defines the main(){...}-routine which outputs firstly a comment line and the guards:

main(){
    <+>
:::://This file was generated by StateMGen - States.genH1
::::#ifndef __<&outfile.name()>_h__
::::#define __<&outfile.name()>_h__

The result of that is

//This file was generated by StateMGen - States.genH1
#ifndef __exampleSimpleStates_h__
#define __exampleSimpleStates_h__

Then a typedef struct is generated which contains all state variables. This struct is intent to embedd in the user's data:

::::/**This struct contains all data which are necessary in the generated code for the state processing. */
::::typedef struct <&stm.zbnfSrc.variables.StateSubStruct>_t
::::{<.+>
  for(state:stm.rootStates) {
    <+>
::::  /**Contains the state identifier for nested level with history or parallel states. */
::::  int state<&state.stateId>;<.+>
    if(state.auxInfo.hasTimer) {
      <+>
::::::  /**Variable to count down a time transition. */
::::::  int timer<&state.stateId>;<.+>
      }
  } //for
    <+>
::::} <&stm.zbnfSrc.variables.StateSubStruct>;

It produces in the headerfile:

/**This struct contains all data which are necessary in the generated code for the state processing. */
typedef struct State_ExampleSimpleData_t
{
 /**Contains the state identifier for nested level with history or parallel states. */
 int statetop;
 /**Contains the state identifier for nested level with history or parallel states. */
 int stateWork;
 /**Contains the state identifier for nested level with history or parallel states. */
 int stateActive1;
 /**Variable to count down a time transition. */
 int timerActive1;
 /**Contains the state identifier for nested level with history or parallel states. */
 int stateActive2;
} State_ExampleSimpleData;

The name of the typedef struct is taken from the source code:

class States
{
 inline void variables() {
   char* StateSubStruct = "State_ExampleSimpleData";

The name of the defined variables in the struct is taken from the names of the non-flat composite states in the source. That are states which are either a parallel state or which has a history. That properties are determined in the prepare process. A non-flat-composite state is part of the List rootStates in the ZBNF/examples_Zmake/StateMGen/result.cmp/exampleSimple.state.dataCheck.dst.html#StateMachine:top - inactive; :

 class Off
 { int statenr_1;

Then one line defines the prototype for the stepStates... method:

::::int stepStates_<&stm.zsrcFile.variables.StateSubStruct>(<:subtext:stateMethodArguments>);

The arguments are generated by a subroutine which is included as subtext:

sub stateMethodArguments()
{
  for(arg:stm.zsrcFile.statefnargs) { <:><&arg><:hasNext>, <.hasNext><.>; }
}

The produced part of the headerfile in the example looks like:

int stepStates_State_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);

Then all state constant definitions are generated:

  for(state:stm.stateList) {
    <+>
::::#define k<&state.stateId>_<&stm.zbnfSrc.variables.StateSubStruct> <&state.auxInfo().zsrcState.stateNr><.+>
  } //for

It writes in the generated code:

/**All state konstant identifier: */
#define kOff_State_ExampleSimpleData 1
#define kReady_State_ExampleSimpleData 0x21
#define kRunning1_State_ExampleSimpleData 0x41
#define kRunning21_State_ExampleSimpleData 0x45
  .....

It may be possible to define this constants in an enum maybe with definition of an separated enum definition for each state variable. This can be done by too with this script, not done yet. An enum is better to assert correct assignments to the state variables.


6.4 The generated c-file, definitions

Topic:.StateMGen_en.gen..

The generation script zbnfjax/zmake/States.genC1.jzgen.html for the C-File starts with the definition of some script variables which are used inside the script:

Filepath outfile = &sOutfile; ##sOutfile is defined in the java calling environment: path to the out file which is written by <+>...<.+>

String stateMethodSuffix = stm.zbnfSrc.variables.StateMethodSuffix;

Class classStateComposite = org.vishia.states.StateComposite;   ##used for instanceof check
Class classStateCompositeFlat = org.vishia.states.StateCompositeFlat;   ##used for instanceof check
Class classStateParallel = org.vishia.states.StateParallel;  ##used for instanceof check

The first one outfile is the same as in the zbnfjax/zmake/States.genH1.jzgen.html. The stateMethodSuffix is often used, it comes from the source code

class States
{
 inline void variables() {
   .....
   char* StateMethodSuffix = "_ExampleSimpleData";

The last three classState... variables are instances of java.lang.Class and it are used to check the instance of a state in the script. You'll see: Not only the data can determine the generation, the type of an instance can used as input too.

Furthermore the C-generation script starts with the main(){...} routine which determines the text of the generation result:

main(){
   <+><:s>
:::://This file is generated from StateMGen.java
::::<.+>
 for(includeline:stm.zbnfSrc.includeLines)
 { <+>
::::#include <&includeline><.+>
 }
   <+>

:::

:::#include "<&outfile.name()>.h" //include the generated header file.

:::

It produces the #include-lines quasi as copy from the source script and at least the generated header file:

//This file is generated from StateMGen.java
#include "ExampleSimpleData.h"
#include <stdio.h>

#include "exampleSimpleStates.h"  //include the generated header file.

The next statements of the main-routine in the States.genC1.jzgen generates all prototypes for entry- and exit-routines.The prototypes for the trans-routines are not necessary because the any trans-routine is called only one time in the following state-switch-routine.

:::://all entry-prototypes:<.+>
  for(state: stm.listStates)
  {
    <+>
::::void entry_<&state.stateId><&stateMethodSuffix>(<:subtext:stateMethodArguments>);<.+>
  }
    <+>
::::<.+>
  for(state: stm.listStates)
  { <+>
::::void exit_<&state.stateId><&stateMethodSuffix>(<:subtext:stateMethodArguments>);<.+>
  }

It produces in exampleSimpleStates.c:

 //all entry-prototypes:
 void entry_Off_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
 void entry_Ready_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
 void entry_Running1_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
 .....
 void exit_Off_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
 void exit_Ready_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
 void exit_Running1_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
 void exit_Running21_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
 void exit_Running2_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);

6.5 The generated c-file, entry- and exit-actions

Topic:.StateMGen_en.gen..

Yet the generation script contains as part of main(){...}:

   for(state: stm.listStates)
   { Obj zsrcState = state.auxInfo().zsrcState;
     if(state ?instanceof classStateParallel) {
       .....
     } else {  //normal, non parallel, composite or simple:
       <+>
 ::::::
 ::::::
 ::::::
 ::::::static void entry_<&state.stateId><&stateMethodSuffix>(<:subtext:stateMethodArguments>)
 ::::::{ //genStateM: entry StateComposite or StateSimple. <.+>
       if(state.rootState) { ##check whether it is not null. It is null on a parallel state or the stateTop.
         <+>
 ::::::::  <&stm.zbnfSrc.variables.StateInstance>.state<&state.stateCtrl.stateId> = k<&state.stateId>_<&stm.zbnfSrc.variables.StateSubStruct>;<.+>
       }

This code block opens a loop over all state. Any state gets its entry action. In the enty action normally the associated root state's state variable is set with this state as current state. A root state is a non-flat composite state. It is the root for all its sub states. The top state has not a root state. Each root state of a parallel state has not a root state. Therefore its association rootState ==null and nothing should be assigned.

Furthermore it is tested whether the state has a timeout transition. Then the timeCondition was set in the after preparation from the original srcJava_vishiaZbnf/org/vishia/stateMGen/StateMGen.ZbnfTrans#time. The auxInfo() refers the GenStateInfo. A timer is presented by the associated timer...-Variable in the generated state struct. The timer is loaded with the given timeout on entry:

       }
       if(state.auxInfo().timeCondition) {
         <+>
 ::::::::  <&stm.zbnfSrc.variables.StateInstance>.timer<&state.rootState.stateId> = <&state.auxInfo().timeCondition>;<.+>
       } //timeCondition

The timer variable is decremented in an extra routine stepStateTimer_.... Note: it is important that the time will be decremented exactly 1 time per step. Therefore it cannot be processed by the way in the correspondend quest transition. Maybe the timer should be decremented in an extra timer thread or interrupt.

Last but not least the users entry code like given in the source is copied to the generated file with the following operations:

       <+>
 ::::::  <&state.auxInfo().zsrcState.entry.code>
 ::::::  #ifdef __DEBUG_entryprintf_States_Fwc__
 ::::::    printf(" entry <&state.stateId>;\n");
 ::::::  #endif
 ::::::}

In this generation file a debug output is added additionally with conditional compilation. It helps for test.

As example a State in the source with timer condition is shown:

       class Running : protected ExampleSimpleData
       { int statenr_0x40;

         void entry(){ thiz->out = 3; }//.
         void exit(){ thiz->out = 5; }//.

         void trans(int time = thiz->delay){ switchTo(new Finit); }

It produces:

 static void entry_Running_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event)
 { //genStateM: entry StateComposite or StateSimple.
   thiz->state.stateActive1 = kRunning_State_ExampleSimpleData;
   thiz->state.timerActive1 = thiz->delay;
   thiz->out = 3;
   #ifdef __DEBUG_entryprintf_States_Fwc__
     printf(" entry Running;\n");
   #endif
 }

The next statements in the generation script are shown to see how the exit routine is built:

::::::
::::::INLINE_Fwc void exit_<&state.stateId><&stateMethodSuffix>(<:subtext:stateMethodArguments>)
::::::{
::::::  #ifdef __DEBUG_entryprintf_States_Fwc__
::::::    printf("   exit <&state.stateId>;\n");
::::::  #endif<.+>
      if(zsrcState.hasHistory) {
        <+>
::::::::  //It is a composite state with a history state. Mark the state number with the inactive bit:
::::::::  <&stm.zbnfSrc.variables.StateInstance>.state<&state.stateId> |= 0x80000000; <.+>
      } elsif(state.aParallelStates) {
        <+>
::::::::  //It is a composite state without a history state. Set the state number to zero.
::::::::  <&stm.zbnfSrc.variables.StateInstance>.state<&stateComposite.stateId> = 0; <.+>
      }
      <+>
::::::  <&state.auxInfo().zsrcState.exit.code>
::::::}
::::::<.+>

6.6 The generated c-file, transistion routines

Topic:.StateMGen_en.gen..

The routines for the transitions are generated after the enty- and exit-routine for each state:

The routines for the transitions are generated after the enty- and exit-routine for each state: