State machine technology in C, C++ and Java

State machine technology in C, C++ and Java

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.



1 An example - with and without ''state thinking''

Topic:.stateM_en.exam.

The example is a communication process. There are some states of working:

As a special problem a timeout of answer should be handled. In the example the requests should not be repeated automatically on timeout, but new requests should be handled only if either the current requests are finished (all answeres are received) or a timeout is expired.

Thinking without states the problem was resolved as following:

That solution works correctly. Some things are sophisticated: Requests while the communication is pending - how to handle? Is the timeout expired? The execution of the answer in another thread than the preparation of request does not may proper, because of data consistence. But for the concrete requirements it is okay.

Thinking in states it is more clarified:



2 Using an UML-tool, advantages and disanvantages

Topic:.stateM_en.UMLusing.

The UML is a well proper software technology. Using of UML, of course with a good tool, has some advantages. It is without question.

But the decision for using an UML tool influences the whole organisation of software development:

Though the UML technology and some tools are present since 15..20 years, they are not omnipresent for developing of software. Why ...?


3 Event Driven Execution or cyclical

Topic:.stateM_en..

In cohesion with statemachines or statecharts in software the Event Driven Execution is used often.

An event is given if any application or part of application (a thread) receives data to execute. The data were transmitted from another part.


An Event Object is an instance of a type derived from java.lang.EventObject (in Java language) which contains information. In that meaning an Event Object is a message really.

There are some rules for event handling:

Events can be send from one thread (invokes any routine to send, enqueues the event) to another thread (dequeues and executes). Events can be send from one application of the same system to another application (system process) using interprocess communication mechanism for example shared memory, dual port RAM.

Event Objects can be send via communication in networks. Java offers the serialize possibility for data which can be used for that. Event handling is possible for large dispensed (distributed) systems.


Advantage of the event concept as interface between applications or part of a program:

The event interface is extrem universal. Any event data type is derived from java.lang.EventObject. It is the only one data type which should be known in the interface. The method for transmitting is

sendEvent(EventObject data, int sizeData);

More is not necessary. Because the concept of serializing in Java a communication layer can prepare the data for example for UDP-Communication via ethernet. For shared memory communication the size of the data are possible to detect using reflection mechanism in Java. Other programming languages (especially C) may need only the size of data as second argument sizeData. Serializing in C is very simple - it uses the byte image of the data immediately.

Disadventage:

The advantage is the disadventage. Because the interface is extrem universal, it does not allow recognizing what and how. The interface does not document the data exchange and control flow.

Therefore: Use an universal event interface between applications to save effort or hide information but Use dedicated method-calls which may deal with events internally for the application-internal flow.

Cyclically execution, conditions and polling

In some ordinary publications the alternative to event driven programming is named as polling (cyclically quest) which would need some more calculation time, which is ineffective therefore. That's wrong. Polling is done with a cycle time usually. The cycle time is determined by the thing itself. If for example a control unit works cyclically, it checks in its own cycle whether conditions are met. It is insignificant whether events are used - the queue would be checked in the controller cycle - or conditions are tested in any cycle. Because the effort to enqueue and survey an event in comparison with testing a simple bit - the cyclically check of conditions may be a better decision, especially for embedding programming.


4 Classes for common useable event handling

Topic:.stateM_en.EventClass.

Events are established in Java firstly for graphical programming. In AWT (Advanced Widget Toolkit) a class java.awt.Event war created in the Version 1.0. This class is now designated as obsolete, instead a common super class java.util.EventObject was defined in Version 1.1 which is extended from java.awt.AWTEvent now. Other frameworks use the common class java.util.EventObject too for example java.beans.beancontext.BeanContextEvent. That are specializations.

For a common approach an event class org.vishia.event.EventWithDst is defined which has two extensions:


4.1 Data of the basic event instance EventWithDst

Topic:.stateM_en.EventClass..

The event based on the java.util.eventObject. Therefore an


4.2 Reusing of event instances

Topic:.stateM_en.EventClass.reuse.

The idea comes from embedded programming usual in C. In Java usual new instances were created whenever an event is necessary. If a source of events sends new events in a short time interval, for example for status messages to show, but the destination does not work yet, then the queue of events is overfilled and the memory of the system peters out.

Usual events are used in a ping-pong kind. If one event was sent, the receiver sends anything back etc. If the back event is missed, a timeout repetition is approriate, but for a longer time interval. For that only two events are necessary.

A sent event can be reused for the timeout repetition too: It can be presumed that it is stored in its destination queue which is not processed. Dequeue it and resuse! Only for a possible coincidence the event won't be found in the queue because it is processed just now. Then the event cannot be reused. In that situation the timeout repetition is just not appropriated. Either skip it, or wait for a short time with a synchronized mechanism.

The capability of reusing is build in the basic event type EventWithDst independent whether it is need or not. That is because the decision about reusing does not depend on the type of the event, it does depend on the usage in the application. The effort is less, no additional data are need, only a few methods for reusing are given:


4.3 The event family and time orders

Topic:.stateM_en.EventClass.events.

srcJava_vishiaGui/org/vishia/gral/base/GralGraphicTimeOrder:

The GralGraphicTimeOrder is the base class for orders which should be execute in the graphic thread of the GRaphic Adaption Layer see srcJava_vishiaGui/org/vishia/gral/package-summary. The method executeOrder() have to be overridden by an implementation class with the functionality which should be execute in the graphic thread. That instance is enqueued in the queue of the graphic thread if the time is its turn. A waiting time is managed by the EventTimerThread.

It is applicated in the following form: The content of a widget (a text field etc.) can be changed in any thread but only with setting the propriate information such as a new color as value, a text as String, the state of a button as boolean etc. Another thread must not invoke graphic routines of a SWT graphic system and it should not invoke routines of AWT because thread safety. After setting the information the existing instance of the TimeOrder is activated with TimeOrder.activate. It, that is executed in the graphic thread and change the graphical widget. It can be activated with a delay for example 100 ms to save calculation time for thread switching. For example 100 widgets are changed in one step. It is not necessary to force a thread switch for any widget. Firstly write all information. Wait a time because more information can be changed furthermore. Then refresh the graphic. A time of 100 ms is short enaugh for most of graphical data presentations. A time of 10 ms is long enough to prevent unnecessary thread switches.

srcJava_vishiaBase/org/vishia/event/TimeOrder:

The TimeOrder can be used in any application to organize a delayed or therewith cyclical invocation of a routine in the timer thread. The functionality should be programmed in the overridden method srcJava_vishiaBase/org/vishia/event/TimeOrder#executeOrder().

A time order routine can be written as instance of an anonymous inner class:

 @SuppressWarnings("serial")
 TimeOrder myTimeOrder = new TimeOrder("name", threadTimer) {
   @Override protected void executeOrder(){
     //action code
   }
 };

The time order is mananged especially by the srcJava_vishiaBase/org/vishia/event/EventTimerThread and executed either in the event thread or maybe in any other thread. For the second case an EventConsumer routine is necessary to enqueue the time order in the other thread. An example is contained in srcJava_vishiaBase/org/vishia/event/test/TestTimeOrder. This case is used for the GralGraphicTimeOrder to execute the graphic action in the graphic thread:

private EventConsumer enqueue = new EventConsumer()  {
  @Override public int processEvent(EventObject ev)
  { execThread.addOrder((TimeOrder)ev);  //casting admissible because special using.
    return 0;
  }
};

TimeOrder order = new TimeOrder("name", enqueue, threadTimer) {
  //...implementation
}

srcJava_vishiaBase/org/vishia/event/EventTimeout:

The EventTimeout is the super class of the TimeOrder but also a ordinary event especially for a timeout. In a srcJava_vishiaBase/org/vishia/states/StateMachine there is one persistent instance for one parallel state or the top state which is used from the active state if a timeout transition is necessary.

To activate a timeout event or a time order from any thread only one of the following methods should be called.

myTimeOrder.activate(milliseconds);
myTimeOrder.activateAt(date);       // with an absolute time stamp.
myTimeOrder.activateAt(date, last);// define a last execution time stamp.

It is possible that the time order should be deferred on a repeated call of activate(milliseconds) some milliseconds later. The time order is executed only one time even the activate(...) is called more as one times before the time is elapsed. To prevent a deferring until the cows come home by repeated invocations of activate(delay) it is possible to set a latest time with srcJava_vishiaBase/org/vishia/event/EventTimeout#activateAt(long, long).


srcJava_vishiaBase/org/vishia/event/EventWithDst:

It is the base class of all this event types. An instance knows a destination where the event is processed: srcJava_vishiaBase/org/vishia/event/EventConsumer and optional a srcJava_vishiaBase/org/vishia/event/EventTimerThread where the event is stored and its execution is invoked.

srcJava_vishiaBase/org/vishia/event/EventCmdtype:

This event base class knows a field for a command which is an enum type. The enum type is used as generic argument. An derived instance should define the generic enum type in its extends statement:

class MyEventType extends EventCmdtype<MyEnumCmd> {
  //some more data are possible.
}

Then the

MyEnumCmd cmd = myEventType.getCmd();

returns the proper type of cmd.

srcJava_vishiaBase/org/vishia/event/EventCmdtypeWithBackEvent:

Events are used often with back events: One instance sends an event, the other instance responses with the proper counterpart which may have another cmd enum type. The requester can create such an double-event. The responder can use the opposite event instance from the received event. A derived event with its opponent is defined like:

/**defines the class for the callback event. The opponent type is MyEvent. */
class MyEventBack extends EventCmdtypeWithBackEvent < EnumCmdBack, MyEvent>
{
  /**package private or private constructor. */
  MyEventBack(EventConsumer callback) {
    super(null, callback, myExecutionThread, null); //here without opponent, just unknown.
  }
}
/**defines the class for the forward event. The opponent type is MyEventBack. */
class MyEvent extends EventCmdtypeWithBackEvent < EnumCmd, MyEventBack>
{
  public MyEvent(EventConsumer dst, EventConsumer callback) {
    //creates the back event as opponent in super invocation;
    super(null, dst, myExecution, new MyEventBack(callback));
    getOpponent().setOpponent(this); //set the backward opponent reference
  }
}

/**The event with the callback event as opponent. */
MyEvent ev = new MyEvent(responder, callback);

The callback event can be gotten from the received event and then used to send back:

EventConsumer responder = new EventConsumer() {
 @Override public int processEvent(EventObject evArg){
   assert(evArg instanceof MyEvent);
   MyEvent ev = (MyEvent)evArg;
   MyEventBack eventBack = ev.getOpponent();
   eventBack.sendEvent(EnumCmdBack.theCmd);
 }

Another usage for the double event is: If an event is sent in a statemachine for itself to force an non-Run-To-Complete continuation, the current event is used yet and a second event is need. Only one event instance of a EventCmdtypeWithBackEvent is provided. One of them is free for usage always:

 /**The event type for intern events. One permanent instance of this class will be created.
  * The opponent will be used alternately because 2 instances may need contemporary. */
 public final class EventInternal extends EventCmdtypeWithBackEvent<CmdIntern, EventInternal>{
   private static final long serialVersionUID = 0L;
   /**The constructor to create the double event. */
   EventInternal(EventConsumer dst, EventTimerThread_ifc thread){
     super(null, dst, thread, new EventInternal(dst, thread, true));
   }
   /**Creates a simple event as opponent. */
   EventInternal(EventConsumer dst, EventTimerThread_ifc thread, boolean second){
     super(null, dst, thread, null);
   }
   /**Sends either with this or the opponent, one of them is able to occupy always.
    * The other one may in use yet. */
   public boolean sendEvent(CmdIntern cmd, boolean requested) {
     final EventInternal ev;
     boolean bOk;                  //evSrc from outer class.
     if(bOk = this.occupy(evSrc, false)) {
       ev = this;
     } else {                      //if this is in use:
       ev = this.getOpponent();    //the opponent should be able to occupy.
       bOk = ev.occupy(evSrc, requested);
     }
     if(bOk) {
       ev.sendEvent(cmd);
     }
     return bOk;
   }
 }

5 Timer and event thread

Topic:.stateM_en.thread.

Actions usually need only a few microseconds, less then a millisecond. An action or operation can be triggered by an external event such a keyboard or mouse input by a operator or receiving a telegram via network. An action can be triggered by a timer, maybe cyclically. The frequency of an action is in range of 1 per milliseconds (it's high) or less for typical non fast realtime applications which runs on a PC platform. The expectable response time for an event on a PC operation system may be typically less then 1 ms, but it may be 10..100 ms if the processor is busy for other things or a longer non interuptable operation is executing yet.

It means one thread can execute all event or timer triggered actions one after another because usually the singly action is fast and the frequency and expected response time is low. There is an advantage to execute all actions in the same thread: The actions are not interrupted. It means no synchronize mechanism are need if several actions work with the same consistent data. The synchronize mechanism would need more calculation time as the action itself possibly.


Therefore using one thread for all execution of actions in several instances is the proper decision for event driven execution.

srcJava_vishiaBase/org/vishia/event/EventTimerThread

This class contains a queue for all events. It executes the events in order of queueing. It supports time orders to execute.


An event should be initialized with the destination respectively the consumer and the thread:



MyEventWithDst myEvent = new MyEventWithDst(source, dst, myEventTimerThread, otherData);

The the event can be sent which enteres it in the queue in the EventTimerThread instance:

myEvent.sendEvent();
...invokes:
myEventTimerThread.storeEvent(myEvent);

The reference to the thread may not need to store in the event instance if the application invokes thread.storeEvent(event) immediately. But this reference is need if the event will be recalled from the queue because it should be used newly with new data if it is not processed yet:

myEvent.occupyRecall(...); //needs knowledge of the storing thread

It is possible to enqueue the event in one queue, dequeue it to process but enqueue it in another queue. Then the currently storing instance of type srcJava_vishiaBase/org/vishia/event/EventTimerThread_ifc can be noted in the event to enable recalling.

Timer thread:

The same instance and thread is used for queueing and starting the processing of an event and for managing time orders. An event may be an timeout event. Then a timer is used. If the time is elapsed then the event will be processed in this thread as event thread.

But the concept is not limited to handle timeout events. Another aproach is managing and execution of srcJava_vishiaBase/org/vishia/event/TimeOrder. It is based on the srcJava_vishiaBase/org/vishia/event/EventTimeout because they are handled from the EventTimerThread in the same way. A TimeOrder instance does not need a destination respectively consumer. It can be act as consumer in its own behalf. An event is only a carrier of data without functionalty. A TimeOrder has a functionality. See 4.3 The event family and time orders.

A time order is also executing in the EventTimerThread. But it is possible to dequeue the TimeOrder and process it like an event if a destination EventConsumer is given by construction. The processing routine can enqueue the TimeOrder in any other queue to process it in another thread. Then the time management thread and the executing thread is different. This is used for the organisation of graphical orders. They are delayed in the timer thread and then executed in the graphic thread.

A time order has its execution time. This time is stored as absolute millisecond timestamp in the TimeOrder instance. The srcJava_vishiaBase/org/vishia/event/EventTimerThread contains a queue of all TimeOrder. A TimeOrder can be deferred by reenter in the timer queue. Then its execution time is deferred only.


6 A framework for state machines in Java

Topic:.stateM_en.stmJava.

The goal is: Working with state machines should be simple but powerfull. The properties of UML-like state machines with parallel and nested states and event-driven mechanism should be available and used, but without effort for writing code and with capability for debugging.


6.1 Pattern

Topic:.stateM_en.stmJava.pattern.

The package org.vishia.states /Jstm/ offers some classes which can be applied in the users code. The package has dependencies only to less other classes in the same software component (srcJava_vishiaBase) and it is available with the LPGL open source software license. It means everybody can use it.

The states are defined as classes. The following code lines are necessary:

import org.vishia.event.*;
import org.vishia.states.*;


class MyStates extends StateMachine
{ MyStates(EventTimerThread thread){ super("name", thread); }

  class StateA extends StateSimple { ....}
  class StateB extends StateComposite {
    class StateB1 extends StateSimple { ....} //inner state
    class StateP extends StateParallel {
      //container for parallel states
      class StateP1 extends StateComposite {
        class StateP1A extends StateSimple {....}
        ...
      }
      class StateP2 extends StateComposite {
        class StateP2X extends StateSimple {....}
        ...
      }
    }
    ....
  }
};

It defines the state machine. Anywhere in the code (in constructor usually) an instance should be created:

EventTimerThread thread = new EventTimerThread("thread");
MyStates states = new MyStates(thread);

The event driven statemachine runs in a thread, which can used for other instances of other Statemachines too.

Inside the state-classes transitions and actions are defined:

 class StateA extends StateSimple
 {
   final boolean isDefault = true;  //default-state
   @Override protected int entry(EventObject ev) {
     System.out.println("Entry in state A");
     return 0;
   }
   @Override protected void exit() {
     System.out.println("Exit from state A");
   }
   Trans transTo_B1 = new Trans(StateB.StateB1.class);
   Trans transParallel = new Trans( StateB.StateP.StateP1.StateP1A.class
                                  , StateB.StateP.StateP2.StateP2X.class) {
     @Override protected void action(EventObject ev) {
       System.out.println("action of transition StateA -> Parallel P1A, P2X");
     }
   };
   @Override protected Trans checkTrans(EventObject ev){
     ....
   }
 }

A state class can define an entry and exit method with the applications code if necessary. All transitions are defined as transition instance. The name of the instances are free. A well style is cause_DestinationState.

The destination state of a transition is given as argument in the constructor as java.lang.Class-instance of the state-class. For a fork-transition to more as one parallel destination states more as one argument is used.

Trans transTo_B1 = new Trans(StateB.StateB1.class);

A transition instance as derived anonymous class can contain an action method.

To check the transitions the checkTrans(EventObject ev) method is used.

   @Override protected Trans checkTrans(EventObject ev){
     if(ev == evA) {
       return transTo_B1.eventConsumed();
     } else if(ev instanceof EvCmdX && ((EvCmdX)ev).getCmd() == CmdX.cmdY) {
       transParallel.doExit();
       //maybe code here to execute as transition code after exit.
       return transParallel.eventConsumed();
     } else {
       //maybe code here to execute cyclically in this state if the state machine will be invoked cyclically
       return null;
     }
   }

The checkTrans method of a state is executed if that state is active and the state machine are invoked. The user can set a debug-breakpoint in a special state with a special condition or write a debug output (System.out.println("...")) to check difficult and selten situations.

This method should check only the conditions for the transition and select the transition. If a condition tests and uses the event then the method eventConsumed() should be called. It sets the bit for mEventConsumed it a temporary variable in the transition which is evaluated by the state engine.

return theTransition.eventConsumed();

It is possible to add a transtion action either simply, executed before the exit-state is done, or after calling

 transXYZ.doExit();
 my.transition.code();

The last one is UML-conform. The exit and entry organisation to the states in the correct hierarchie is done by the state engine in the StateSimple class with the information of preparation phase.

If a timeout is need in a state a timeout transition should be defined. The shown action in the timeout instance is optional. The timeout should be checked in the checkTrans-routine. (An older version of the state engine had checked and execute the timeout in its core routines. But then the timeout is more difficult to debug - set a breakpoint).

The check whether the event is a timeout event exactly for this state is done by the isTimeout(...) routine of this state. Note that another timeout can be received too. Debugging hint: The class StateSimple has a field evTimeout and a field transTimeout. Both is set in the preparation phase because the Timeout transition. The event is compared with the evTimeout instance which is used and activate on entry of the state.

  class StateWaitAnswer extends StateSimple
  {
    Timeout transIdle_Timeout = new Timeout(5000, StateIdle.class)
    { @Override protected void action(EventObject ev)
      { System.err.println("timeout");
      }
    };
    @Override protected Trans checkTrans(EventObject ev)
    { if(
         ...
      } else if(isTimeout(ev)) {
        return transIdle_Timeout;
      } else return null;
    }

A join transition is written as instance of TransJoin with the destination state(s) in its constructor and the source states of the join in the method srcStates(...) called after construction. This join transition is moved to all source states automaticly and checked there, without any additional programming effort.

class StateP extends StateParallel {
  ...
 TransJoin to_off = (new TransJoin(StateOff.class))
                    .srcStates( StateActive2.StateShouldOff.class
                              , StateActive1.StateFinit.class
                              );

A choice transition is written by an instance of TransChoice which contains the further transitions and a choice(EventObject) method to check the choice. This method should be invoked inside the superior checkTrans(...) method:

 public TransChoice on = new TransChoice() {
   Trans cont_history = new TransDeepHistory(StateWork.class);
   Trans ready = new Trans(StateWork.StateReady.class);
   @Override public Trans choice() {
     if(cond.cont) return cont_history;
     else return ready;
   }
 };
 Trans otherTrans ...
 @Override protected Trans checkTrans(EventObject ev) {
   if(  ev instanceof EventA ) return on.choice().eventConsumed();
   else if(...) return otherTans;
   else return null;
 }

Note: stateMachine is an association in any state, refers the srcJava_vishiaBase/org/vishia/states/StateMachine.

If the programmer should get an overview over his/here states, for example the outline tree of an Eclipse IDE can be used. It shows:


The transitions are designated with the trigger or another significant name and the destination state. In this kind the outline tree shows the state flow maybe sufficient.


6.2 How does it work - missing data via reflection

Topic:.stateM_en.stmJava.refl.

Nowhere the quest of all transitions is programmed. The states are only given as classes, not as instances. Nothing of them is written in source code. What's the matter?

The execution of state quests are contained in the base classes of org.vishia.states of course.

The missing data are created by methods in the base classes of the states which uses reflection mechanism:

The following fields are set by reflecton, able to check by debugging:

The state Cxy may be the exit state, C is the common state which is not left. The array entryStates contains:

The order of entry states followes the indices in the srcJavaPriv_vishiaBase/org/vishia/states/StateSimple#statePath of all 3 entry states. The first index is 2 because at [1] the common not left state is referred. All states of [2] are entered. It is only Ap because this is a common state for the fork-path. Then all states of [3] are entered etc. The order is then:

Ap, A1, Bp, B1, B2, B12, B25, B251

That is UML-conform. Another order may be also UML-conform and also able to expect, firstly the full first path, then the second etc. which is:

Ap, A1, Bp, B1, B12, B2, B25, B251

The order of entering effects on the order of enter() actions of the state. It should not be meaningfull for the application because the UMl does not define the order. The effect of following run-to-complete transitions are not affected by this order because that operations are done after this transition switch.


6.3 Exception handling in event processing and state transitions

Topic:.stateM_en.stmJava.except.

Catch any cycle of event execution:

In a well tested software exceptions should only thrown if they are able to expected. For example a FileNotFoundException on creation of a FileReader.

If a software is under construction programming errors are able to expect. If a software is well tested and exceptions are never expected it is a less effort to handle exceptions with a simple output on System.err which can be redirected to any log output. That is for the unexpected case there is an exception nonetheless.

If an exception is occured on the execution of an event and this exception is not catched then the thread for all event execution is aborted. The whole execution of all events after them is abandoned. It is not possible to continue working to repeat the situation and find out the cause. Therefore the loop of the event execution from the event queue needs a catch for all exceptions:

@Override public void run() {
  while(cont) {
    EventObject ev = queue.offer();
    if(ev !=null) {
      try {
        process(ev);
      }
      catch(Exception exc){
        CharSequence text = Assert.exceptionInfo("EventTimerThread unexpected Exception ", exc, 0, 50);
        System.out.append(text);
      }
    }
  }
}

This is part of org.vishia.event.EventTimerThread.The Assert.exceptionInfo assembles a text which contains the stacktrace of the given number of levels (max. 50 in this example) as one long line. This is proper to write it in a log file or in a line in any console output which wraps the line.

If the exception is thrown in the middle in the run to completion of any state switch - that state switch is incomplete. It is possible that data structures are inconsistent hence. Nevertheless it is possible to repair the situation for example with a restart event or such ones.

Catch any transition:

If the execution code of a transition or the user's entry- or exit code fails then the singly transition or action can be thrown. In that case the state execution is correct. Only the users data can be confused because the aborted execution. That is proper if unknown exceptions are expect-able because the software is not well tested in all situation:

Trans trans;
try{ trans = checkTrans(ev); }
catch(Exception exc){
  if(stateMachine.permitException){
    StringBuilder u = new StringBuilder(1000);
    u.append("StateSimple trans exception - ").append(stateMachine.getStateInfo()).append(";");
    if(ev !=null){ u.append("event: ").append(ev.toString()); }
    CharSequence text = Assert.exceptionInfo(u, exc, 0, 50);
    System.err.append(text);
    trans = null;
  } else {
    throw new RuntimeException(exc); //forward it but without need of declaration of throws exception
  }
}
if(trans !=null){
  ....

This code snippet is part of the execution of transitions to find the firing one. If there is an exception, no transition fires and the machine remain in the current state. If the transition code contains the doExit() it is possible that a state is exited but the destination state was not entered. Nevertheless the state machine may be situated in a proper state. With the outputted error text, the current state and some more stimulies after them the mistake is able to encircle often.

The adequate code wrapes the invocation of the users entry and exit actions of the states.


7 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 6.2 How does it work - missing data via reflection): 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.


7.1 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.


7.2 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.


7.3 The generated code for C-Statemachines

Topic:.stateM_en.stmC.genStmC.

The parsing and preparation of the state machine delivers data, the code generation can be done with several different principles. The following style of generation code should be comprehended only as one possibility.

The generated code does not use the known switch-case strategy. Instead: For any state a _StateConst will be generated in the const data area which contains function pointers to checkTrans...() and some other routines. From the view of software stability function pointers in a data area may be a source of unpredictability because any software error can override a function pointer with a faulty value, then a imponderable machine code is executed. But if the function pointer is a part of a const the probability of disturbing a const is some more lesser. If the const data area is protected for write access it is zero. It is possible to check whether a pointer to the ...StateConst is correct. After this check the invocation of a function pointer is safety.

In the translated header file a struct

/**This struct contains all data which are necessary in the generated code for the state processing. */
typedef struct StateMachine_ExampleSimpleData_t
{
  StateBaseGen const* statetop;
  StateBaseGen const* stateWork;
  ....
} StateMachine_ExampleSimpleData;

is generated which contains a reference to the ..._StateConst for any 'StateComposite'. The StateBaseGen is the abstracted part of any ...StateConst. It is defined per code generation because it is specialized for the user's data type defined in

 /* 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

With the UserDataType the type of the function pointer are defined:

/**Function type definition for getting the current state of a composite. */
typedef struct StateBaseGen_t const* StateCurrentRnGen(struct ExampleSimpleData_t* thiz, int event);

/**Function type definition for the generated trans check routine. */
typedef int TransRnGen(struct ExampleSimpleData_t* thiz, int event);

/**Function type definition for the user's exit routine. */
typedef int EntryRnGen(struct ExampleSimpleData_t* thiz, int event);

/**Function type definition for the user's exit routine. */
typedef void ExitRnGen(struct ExampleSimpleData_t* thiz, int event);

/**Function type definition for the exit routine for a composite or parallel state. */
typedef void ExitParallelRnGen(struct ExampleSimpleData_t* thiz, int event, struct StateBaseGen_t const* stateExitLast);

They are used in the StateBaseGen structure type definition:

/**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;

A stateConst, for is defined like:

//It is the path to the nested state inside the top state, used for a history transition, used as info for documentation.
//
static StateBaseGen const* const statePath_Running2_State [] = ////(stateMcHgenC.jzgen: const statePath)
{
 &Work_StateConst.Top
, &Active_StateConst.parallelBase_Work
, &Active1_StateConst.Active
, &Running_State_StateConst.Active1
, &Running2_State_StateConst.Running_State
  //stateDefault ((stateMcHgenC.jzgen: stateDefault-path)
, &Running21_State_StateConst.Running2_State
};
...
//It is the const description data for the state Running2_State, containing the reference to the doTransition... routines.
//
Running2_State const Running2_State_StateConst = //(stateMcHgenC.jzgen: generateStateConst)
{ //The state const head data, type StateBaseGen:
  { signature_State_Fwc, 0x000   //->signature ->id
  , 2        //->ixCompositeState (in statepath)
  , 4   //->ixOwnState (in statepath)
  , {statePath_Running2_State, ARRAYLEN_SimpleC(statePath_Running2_State) }  //->statePath.ptr  ->statePath.length
  , kCompositeFlat_EKindState_StateMachine_Fwc  //->kind  type: EKindState_StateMachine_Fwc
  , null  //no StateComposite, currentState
  , checkTransGen_Running2_State
  , entryGen_Running2_State
  , exitGen_Running2_State
  , null  //no StateParallel, exitParallel
  }
  //The dotrans routines used in the manual written transition routines: (stateMcHgenC.jzgen dotransConst)
, doTransRunning2_State_f1__Running1
};

It shows an deeper nested state in a parallel state bough which is a composite-flat state - an complex example. The statePath_... contains 2 parts:

The ..._StateConst starts with the values of the StateBaseGen. That only is used for exit and history entry. The first element is a signature, defined with a reference to a const text. While referencing a _StateConst this first element is checked whether it refers the signature. If not, then it is a fatal software error caused by another bug. Then the function pointers are not used. The possibility that a pointer to a StateBaseGen refers a faulty memory location and this memory location refers exact this memory address, which is not used for another things, or it contains an integer value with exact the adequate value, is 1 / 2 power 32. Very less. Only as result of a bug which's possibility should be less. On the other hand, the prohability to detect such an faulty pointer while testing is high.

The next values contains indices to the statePath, the kind of the state, and the reference to the state routines (function pointer) which are used in the generated code.

Process the state machine:

The following function is generated too:

int stepStates_State_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event)
{ StateBaseGen const* stateTop = thiz->state.statetop;
  if(stateTop == null) {
    //do entry.
    TODO: doEntry!
    stateTop = thiz->state.statetop = &Off_State_StateConst.Top;
  }
  return stateTop->checkTrans(thiz, event);
}

This routine should be called from the application either if an event is gotten or cyclically with or without an event. The routine organnizes the first entry to the default state. That's on-entry actions should be called intially. Then the routine calls the checkTrans(...)-routine of the top State. This routine is the manual written routine which's prototype was found by translation. But the reference to this routine is associated by the generated ...StateConst.

//stateMcHgen-Syntax: Any state can have one checkTrans...().routine.
//Its prototype should be defined in the generation input header (ExampleSimpleData.states.h).
//A checktrans routine without prototype is not used.
//
//The prototype should have the same parameter list as the TransFn, but additionally the state reference is given as last argument.
//With that state argument the transition should be selected.
//If the transfunction has an abbreviating parameter list than the generated code does not match, it causes a compiler error.
//
//The generated code contains a checkTrans routine for any state which calls this routine but calls the checkTrans routine
//from all environment states too.
//
//The user documentation for the transition should be given on the prototype because a documentation will be generated with them.
//
int checkTrans_Off(ExampleSimpleData* thiz, int ev, Off_State const* state) {
  if(ev == kOn_Cont_Event_ExampleSimple){
    return state->workHistory(thiz, ev);
  } else  if(thiz->on_ready) {
      //thiz->trans(&states.Work.History, &data->state) | thiz->trans(&states.Work.Ready, &data->state); }
    return state->Work(thiz, ev);
  }
  else return 0;
}

This routine gets the ...StateConst as argument. It calls state->Work(...) which is a generated routine referred in the Off_State_StateConst:

//Transition of Off_State
static int doTransOff_State_Work(struct ExampleSimpleData_t* thiz, int event) //(stateMcHgenC.jzgen dotrans)
{ int trans = 0;
  //state: Off_State
  //exit the current state of this composite till given level: (stateMcHgenC.jzgen: doTrans-exit)
  exitGenState(thiz, event, thiz->state.statetop, 0);
  transAction_Work(thiz);  //(stateMcHgen.jzgen: transAction)
  thiz->state.statetop = &Work_StateConst.Top; //(stateMcHgenC.jzgen: set state)
  entry_Work(thiz);  //(stateMcHgen.jzgen: call entry)
  #ifdef __DEBUG_entryprintf_States_Fwc__
    printf(" entry Work;\n");
  #endif
  thiz->state.stateWork = &Ready_State_StateConst.Work; //(stateMcHgenC.jzgen: set state)
  #ifdef __DEBUG_entryprintf_States_Fwc__
    printf(" entry Ready_State;\n");
  #endif
  trans |= mTransit_States_Fwc;
  return trans;
}

This routine calls exitGenState(...) to exit the current state maybe with exit sub states. Then the state variable is changed with the new ...StateConst. Then all entry_()-Routines in the correct order are invoked if there are existing (the prototype was found by generation). A printf is generated for better debugging. That can be changed if the generation script is changed for that.

exitGenState(...):

The doTrans... routine is generated for that state where a transition was found in the struct ...State. It is possible that it is not a leaf state but a composite (enclosing) state. It means that not only that state should be exiting but the current state which is known only at runtime, not at generation time. Therefore the exitGenState(...) routine starts with the found current state and calls exit for all states of the state path till the common state of transition. The last state to exit is given by the last argument, the index in state path. In this example it is 0 because the last exting state is at top level.

The exitGenState(...) routine is a standard algorithm but it is generated to regard the user data type:

//The generated exit routine calls the exit of the current state of composite
//and exits all states in the hierarchie till exclusive the parent composite state.
//It is used when the parent composite state is exited. //(stateMcHgenC.jzgen: exitComposite)
static void exitGenState(struct ExampleSimpleData_t* thiz, int event, StateBaseGen const* stateCurr, int level) {
  if(stateCurr != null) {
    if(stateCurr->kind == kComposite_EKindState_StateMachine_Fwc) {
      StateBaseGen const* stateCurrent = stateCurr->stateCurrent(thiz, event);
      //exits the current state(s) of composite, recursively for all composites. But not the composite itself.
      exitGenState(thiz, event, stateCurrent, stateCurr->ixOwnState +1);
    }
    //int levelComposite = stateCurr->ixCompositeState;
    //if(levelComposite < level) {
    //  levelComposite = level;  //no more
    //}
    StateBaseGen const* stateExit = stateCurr;
    StateBaseGen const* stateExitLast = null;
    int ixStatePath = stateCurr->ixOwnState;
    do {
      if(stateCurr->kind == kParallel_EKindState_StateMachine_Fwc) {
        stateExit->exitParallel(thiz, event, stateExitLast);
      }
      stateExit->exit(thiz, event);
      stateExitLast = stateExit;
      stateExit = stateExit->statePath.ptr[--ixStatePath];  //the enclosing state.
    } while(ixStatePath >= level); //Composite);
  }
}

Because a StateComposite has an own current state the routine is called recursively for a current composite state. Then a do-while loop is processed from the ixownState in the statePath till the given level of the last exiting state. If one of the state in the path is a StateParallel all states of any parallel bough should be exiting. Therefore the generated and referred routine exitParallel(...) is called. This routinen knows by generation the parallel boughs. Because one bough, from the detect current state, is already exits, it is given as argument for exclusion.

In the simple case only the current state->exit(...) routine was called.

History entry:

The history entry uses the saved current states of a before-inactive StateComposite to re-enter. This routine uses the statePath to enter all states of the path till the current history states:

//Prototype for the doEntry execution in deep history.
static int doEntryDeepHistory(struct ExampleSimpleData_t* thiz, int event, StateBaseGen const* stateDst);
//Routine to entry in the old current state(s), invoked if a History pseudo class is given as destination in a transition.
//Note: it is only visible in this compilation unit, static and without prototype in a header.
//Note: It is the same algorithm as in Java: org.vishia.states.StateComposite.doEntryDeepHistory(...)
//  but the argument types are generated, in Java there is an automatic aggregation to the enclosing class.
//
static int entryDeepHistory(struct ExampleSimpleData_t* thiz, int event, StateBaseGen const* stateComposite) {
  int cont;
  StateBaseGen const* stateDst = stateComposite->stateCurrent(thiz, event); //get the old current state
  if(stateDst == null) {
    //on first history entry, an history state is not known.
    stateDst = stateComposite->statePath.ptr[stateComposite->ixOwnState+1]; //the default state is the last state in statePath (stateMcHgenC.jzgen: stateDefault)
  }
  cont |= doEntryDeepHistory(thiz, event, stateDst);  //stateDst is the old current state of this composite.
  return cont;
}
//Routine to execute the entry in deep history.
//Note: It is only visible in this compilation unit, static and without prototype in a header.
//Note: It is the same algorithm as in Java: org.vishia.states.StateComposite.doEntryDeepHistory(...)
//  but the argument types are generated, in Java there is an automatic aggregation to the enclosing class.
//
static int doEntryDeepHistory(struct ExampleSimpleData_t* thiz, int event, StateBaseGen const* stateDst) {
  int cont = 0;
  int ix;
  StateBaseGen const* const* statePath = stateDst->statePath.ptr;
  int ixOwnState = stateDst->ixOwnState;  //The last position for the statePath in statePath
  for(ix = stateDst->ixCompositeState+1; ix <= ixOwnState; ++ix) {
    StateBaseGen const* stateEntry = statePath[ix];
    cont |= stateEntry->entry(thiz, event);
  }
  if(stateDst->kind == kComposite_EKindState_StateMachine_Fwc) {
    //if the current state is a composite too:
    entryDeepHistory(thiz, event, stateDst);
  }
  else if(stateDst->kind == kParallel_EKindState_StateMachine_Fwc) {
    //If the current state is a StateParallel, enter all parallels with deep history:
    for(ix = stateDst->ixOwnState +1; ix < stateDst->statePath.length; ++ix) {
      StateBaseGen const*  stateP = statePath[ix];
      cont |= stateP->entry(thiz, event);   ////entry in the parallel state bough, composite or simple
      if(stateP->kind == kComposite_EKindState_StateMachine_Fwc) {
        cont |= entryDeepHistory(thiz, event, stateP);
      }
    }
  }
  return cont;
}

The code is similar to the Java code in srcJava_vishiaBase/org/vishia/states/StateComposite#entryDeepHistory(java.util.EventObject).

TODO explain

OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD
OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD OLD

This style of code generation uses a switch-case..-construct as core processing of a state machine, how it is used often for hand-written state machines:

 switch(stateVariable) {
   case stateA:
     if(conditionXYZ){ //switch to stateB...
     ....

To keep overview all entry-, exit- and trans- actions are contained in extra methods (or C-functions).But that C-functions are written as static or inline. Therewith the compiler can expand the machine code without calling the sub routine, create a flat code.

A further important decision for code generation is: If a transition fires, then the switch-case quest is repeated till no more transition fires. That is the run-to-complete-principle of state machine's processing. Maybe manual written state machines don't thing about such ones. But it is consequently and correct. Therewith the core construct of state-switching looks like:

int stepStates_State_ExampleSimpleData( struct ExampleSimpleData_t* thiz, int event )
{ int trans = 0;                //set to true if transition has fired.
  int ctSwitchState = 10;     //prevent too many transitions - a endless loop
  do {
    trans &= ~mTransit_States_Fwc;
    switch(thiz->state.statetop) {
      case kStateA: trans |= transXYZ(thiz, event); break;
      case ...
    } //switch
  } while(  trans & mTransit_States_Fwc) //continue checking transitions if one of the has fired, it is run to completion.
         && --ctSwitchState >0);  //prevent hanging
}

The next important decision for code generation is: support parallel states, support states with an history entry. For parallel states any of the parallel bough need in own state variable. For states with an history entry an own state variable is necessary to save the state. But commonly for composite states there is no necessity for one state variable for each composite state. An ordinary composite state without history has the capability that it has inner states, and its transitions have to be regarded. But all inner states can be immaged with one state variable as flat presentation. If the user defined an composite state to pool some states for only one common transition or only for documentation, the generated code should not become more complex.

All state variables for parallel and composite-with-history states are processed in nested switch-case operations. Therewith the code looks like the example see * examples_Zmake/StateMGen/result.cmp/exampleSimpleStates.c.html#stepStates

 do {
   switch(stateVariable) {
     case stateA:
       if(conditionXYZ){ //switch to stateB...

7.4 Generated code with the switch-case variant

Topic:.stateM_en.stmC.C1.

The given scripts with the -c: option in the command line above are the standard scripts used for this example:

The result of the translation are the named C- and Header file and a documentation. The output generated with the standard scripts have the following form. The example shows that code which was generated for the given C++ source code only for this one state of the example. The code is more complex if nested and parallel states are used. Visit the example source and generated files:

Headerfile:

//This file was generated by StateMGen - States.genH1
#ifndef __exampleSimpleStates_h__
#define __exampleSimpleStates_h__
/**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;
 ....
} State_ExampleSimpleData;

The state machine needs one or more as one state variable. The standard generation uses a simple integer variable for the state which is set with several values of a define-macro. This form is common used for state programming in C. A maybe better and faster variant is the usage of function pointers because the switch-case-statement is not necessary then. More as one state variable is necessary for parallel states or states with a history. Use this struct to embedd it into the users data.

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

That is the prototype for the step routine which can be invoked by the application either cyclically or if an event is present in a users queue. Examples for event handling are contained in the example.

#define kOff_State_ExampleSimpleData 1
...

That are the values for any state, which can be evaluated by the application too.

#endif  // __exampleSimpleStates_h__

That is the header file. It contains only public elements which can be used by an application.

The generated C-File:

/*This file is generated from StateMGen.java */

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

The genrated C-file includes all of the headers which are included in the C++ source.

//all entry-prototypes:
void entry_Off_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
...
void exit_Off_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event);
...

The prototypes in the generated C source are necessary because they should be defined before usage.

The entry- and exit method is defined as static (private visible) because they are used only in this source.

static void entry_Off_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event)
{ //genStateM: entry StateComposite or StateSimple.
 thiz->state.statetop = kOff_State_ExampleSimpleData;
 thiz->work = 0;
 #ifdef __DEBUG_entryprintf_States_Fwc__
   printf(" entry Off;\n");
 #endif
}

The entry method contains the association of the state value to the state variable.

INLINE_Fwc void exit_Off_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event)
{
 #ifdef __DEBUG_entryprintf_States_Fwc__
   printf("   exit Off;\n");
 #endif
}

The conditional printf statement is a nuance of the code generation with this script. It may be helpfull for debugging on runtime.

All transitions and the inState-routine in the C++ source are generated into one trans... method with a chain of if(...) in order of the transitions in the C++ source.

INLINE_Fwc int trans_Off_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event)
{ int trans = 0;
 //genStateM: check all conditions of transitions, return on transition with != 0
 if(thiz->on_ready) {
   exit_Off_ExampleSimpleData(thiz, event);
   thiz->work = 1;
   entry_Work_ExampleSimpleData(thiz, event);
   entry_Ready_ExampleSimpleData(thiz, event);
   trans = mTransit_States_Fwc;
 }
 else
 if(thiz->on_cont) {
   exit_Off_ExampleSimpleData(thiz, event);
   entry_Work_ExampleSimpleData(thiz, event);
   trans = mTransit_States_Fwc;
 }
 else
 { //StateMGen: action in state. No transition switched.
   thiz->counter +=1;
 }
 return trans;
}

Last not least a complex stateswitch routine is generated, which contains the switch-case for the states:

int stepStates_State_ExampleSimpleData( struct ExampleSimpleData_t* thiz, int event )
{ int trans = 0;                //set to true if transition has fired.
 int ctSwitchState = 10;     //prevent too many transitions - a endless loop
 do {
   trans &= ~mTransit_States_Fwc;
   switch(thiz->state.statetop) {
              //if the state was entried newly without define an inner state, then the statedefault will be entered now.
              //Note that the default state cannot be entered on entry action because it is unknown in that time
              //whether there will be an entry to a designated state.
     case 0: entry_Off_ExampleSimpleData(thiz, event); //without break, do trans:
     //switch to the current state:
     case kOff_State_ExampleSimpleData: trans |= trans_Off_ExampleSimpleData(thiz, event); break;
     ...
     ...
 } while((trans & mTransit_States_Fwc)    //continue checking transitions if one of the has fired, it is run to completion.
     && thiz->state.statetop !=0  //don't continue if the composite is inactive now.
     && --ctSwitchState >0);     //don't execute too may loops, only a safety check.
 //
 //for all parallel states: switch only if this state is the active one still. Regard a leave of the state from any substate.
 return trans;

}

See article StateMGen for more explaination.


8 Parallel and nested states - UML conform handling of events and RunToComplete

Topic:.stateM_en.Paralnested.

The possibility of parallel and nested states is a known property of UML statecharts. It may be a proper means of expression for states. Look for the exampe:


It is a test example contained in srcJava_vishiaBase/org/vishia/states/example/StatesNestedParallel.java.

Any process can be switched on. The idle state is Off. An event or condition on_ready switches to ready for operation or in the state Ready.

The event or condition start activates two states with two parallel threads. The left thread Active1 controls the process between Running and Finit. In practice there may be some more states. The right thread Active2 controls a user interface for the process. If the process runs and the user press a button offAfterRunning, then the Active-state is left, switches to Off, if the left thread reaches Finit. The user can press that button (repectively send the event or sets the condition) offAfterRunning while the state is Running, it is independent, e.g. parallel.

If the state Finit was reached, and the offAfterRunning was not given, the state switches to Ready. That is because the transition between Finit and Ready which checks the state of the parallel thread.

Furthermore the process can be switched off any time without condition. It is the transition from Work to Off. Additionally the Running process can be aborted only after offAfterRunning It is the transition between ShouldOff and Off

Last not least the process can be switched from Off to the last active state with the History state to continue its work. That is the counterpart to the off transition between Work and Off.

That example is a constructed example which uses all possibilities. It may be proper for practice in any kind.

Event or Condition:

From view to this state machine it is not defined whether the transitions are triggered from events or conditions. Event driven programming comes together with the state machine thinking often, but it is another topic generally. From this view, event or condition is a question of implementation more than a topic of design. Using of events is a decision of software organization more than a decision of the users scope.

The threads of parallel states: From view of the operation system there are user threads. Never it is multithreading of the operation system. The state machine runs checking events or conditions, one parallel state after another in the same operation system thread. Then it waits for the next event or condition check step. If data should be handled consistently, there is not an interrupt between state switch actions. Sometimes that user threads are named fiber.

How does it should be written in Java:

See the source code in srcJava_vishiaBase/org/vishia/states/example/StatesNestedParallel.java.

The parallel states are written like


9 State execution, threads, events or condition

Topic:.stateM_en.event.

From view of the execution of a state machine it is not meanfully whether the transitions checks events or conditions. Event driven programming comes together with the state machine thinking often, but it is another topic generally.

The quest is: Who executes the switching of a state machine, the transition test. There are more as one possibility:

Events and Conditions can be used simultaneously. The execution of a state machine can be forced only by events or cyclically.


9.1 Event-like conditions

Topic:.stateM_en.event..

Using conditions may precipitate a problem: If a condition was set after an action, for example a key was pressed, the state machine reacts on this condition. That is okay. But if the condition is given a longer time (the key is still pressed, a status bit is set continuing) and the state machine enters the same state again, the condition force switching again.

The event driven execution of a state machine is the proper solution for a complex thread system: Any thread changes a condition, itself or another thread sends the event, the event contains data for execution, some other threads may sent events simultaneously. All of the event are written in the same queue in its order of emitting. The writing is executed in any thread but threadsafe for the queue. A java.util.concurrent.ConcurrentLinkedQueue is a proper class for that.


9.2 State Machine's Thread, Fiber, short execution time of transitions

Topic:.stateM_en.event..

All events are evaluated in the same thread. That thread can execute not only one state machine but any number of ones. The advantage of execution of some more state machines in one thread is: There are not problems of thread safety if the state machines works with the data of some other classes concurrently. The execution process which is forced from one event is named fiber, or sometimes user thread. All fibers of the state machines thread are executed one after another without interruption. But on the other hand it means, an execution process of one event is delayed if there are some more events for some other state machines. Usual an execution process should be shortly in time. If an execution duration is for example 1 ms, the expected response time is about 100 ms in maximum and there are about 100 Events simulatneously emitted, it is narrowly. Therefore for deterministic systems especially cyclically controlling processes an event queue may not the proper solution.

A transition should never delayed by any Thread.sleep(...) or Object.wait(...) invocation or by a synchronized[...} operation except that is a guaranteed short waiting action because a high priority thread processes a short action. If one transition is delayed, the whole processing of all state machines are delayed which works in this same event thread.

An implementation for such an event queue thread is given with the class org.vishia.event.EventTimerThread. This class works with org.vishia.event.Event instances which contains a reference to a org.vishia.event.EventConsumer-interface. The org.vishia.states.StateMachine implements that interface to execute it.

If a state machine should be executed then the method

StateMachine.applyEvent(event);

should be invoked. Either the StateMachine was constructed with a given EventTimerThread then the event is stored in the queue and the execution is done in the event thread, or the StateMachine is executed immediately. In the last case the event can bei ommited, null can be given. The StateMachine has not an extra execution line for non-event (only conditional) execution because from view of the state machine's execution an event is also a condion only. But an event can contain data which are forwarded to the actions of the state machine!


9.3 UML conform handling of events and RunToComplete

Topic:.stateM_en.event.RunCompl.

The state machine is executed if any event is given. If the given event cannot be applied to any transition the event is removed after them. It is not stored and applied later again. This is an important mechanism. If an unexpected event is stored and applied later again, an unexpected reaction can be occured. The known deferred event in the OMG.org documentation /omg-uml/ page 587 is a special case only!

If an event was applied to one transition in a sequence of transitions it is not applied to a next transition. It is removed after usage. But an event will be applied to all current parallel states. It is possible that one event switches more as one parallel state. That is UML-conform. See /omg-uml/ page 575.

An event will be applied firstly to the inner state of a composite state. Only if the event is not used there, it is applied to the composite state levels. See /omg-uml/ page 575 Firing priorities:

omg-uml/: In the presence of orthogonal regions it is possible to fire multiple transitions as a result of the same event occurrence as many as one transition in each region in the current state configuration.

If an event has triggered the execution of the state machine then all following transitions which are not event triggered are fired, if there condition (guard) is true. That is done for all parallel states.


10 The full writing rules for Java-Statemachines

Topic:.stateM_en.rule.

===State machines in C and C++===

The

C++


11 Literatur-citiziation

Topic:.stateM_en..