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){
     if(ev == evA) return transTo_B1.eventConsumed();
     else if(ev instanceof EvCmdX && ((EvCmdX)ev).getCmd() == CmdX.cmdY)
       return transParallel.eventConsumed();
     else return null;
   }
 }

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

 @Override protected Trans checkTrans(EventObject ev){
   ....
 }

This method checks only the conditions for the transition, without action or other effect. If a condition tests and uses the event then the method eventConsumed() should be called:

return theTransition.eventConsumed();

If a timeout is need in a state it is sufficient to define a timeout transition only. No more is necessary. The shown action in the timeout instance is optional. It is written very simple:

  class StateWaitAnswer extends StateSimple
  {
    Timeout timeout = new Timeout(5000, StateIdle.class)
    { @Override protected void action(EventObject ev)
      { System.err.println("timeout");
      }
    };

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:

 0     1        2           3          4      5      6  ix in statePath
           +<---Cxy
           |
top    C   +--->Ap --->|--->A1
                       |
                       |--->Bp--->|--->B1--->B12
                                  |--->B2--->B25--->B251

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. It may be recommended to use a C++ program which contains states as classes and all user-specific code for actions, conditions etc. The C++ state machine source can be compiled to check its syntax. It is not to execute.

The source is parsed and translated to a C-code which should be compiled and linked to execute in the target system. An internal preparation of the parsed state machine's data completes the missing information for state switches. It is the same algorithm which is used for Java state machines with identical behaviour. The form of the generated C-code is tunable by changing the JZcmd generation script.

Follow the pattern for C++ StateMachine Source:


7.1 Patten for C++ StateMachine Source, Generated code

Topic:.stateM_en.stmC.pattern.

The user can write and compile this C++ source of the state machine. But that C++ code is not used to run. The compilation is only done to check the syntax with the C++ compiler to detect writing mistakes while editing the source. The translation to the C-sources for the deployment of the state machine is done by a ZBNF parser and JZcmd generator.

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

Look for the pattern or visit the example source file examples_Zmake/StateMGen/src/ExampleSimple.state.cpp.html:

#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;

//Helper should be defined.
void switchTo(void* ...);

class States
{
 inline void variables() {
   char* StateSubStruct = "State_ExampleSimpleData";
   char* StateMethodSuffix = "_ExampleSimpleData";
   char* StateInstance = "thiz->state";
 }
 /**It is the off-state. This part contains a documentation of the state.
  */
 class Off
 { int statenr_1;
   .....
   .....contains nested states.
 };
 ..... contains all states of the top level.
};

That is the frame of a C++ source for the Statemachine.

static void entry_Off_ExampleSimpleData(struct ExampleSimpleData_t* thiz, int event)
{ //genStateM: entry StateComposite or StateSimple.

That class States contains all states of the machine, one state as one inner class definition. Any state has the form:

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

 };

This is a full example for one state of the statemachine in the chapter Parallel and nesting states ....


7.2 Generation of code

Topic:.stateM_en.stmC.gen.

The translation of such a state description C++ code to the C state execution code is done by a Java program:

java org.vishia.stateMGen.StateMGen -i:src/exampleSimple.state.cpp -s:../../zbnfjax/zbnf/StateMCpp.zbnf
-d:result/data_Statem.txt -scriptcheck:result/data_script.txt -c:../../zbnfjax/zmake/States.genDocu.jzgen
-y:result/exampleSimple.topic -c:../../zbnfjax/zmake/States.genC1.jzgen -y:result/exampleSimpleStates.c
-c:../../zbnfjax/zmake/States.genH1.jzgen -y:result/exampleSimpleStates.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:

TODO

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

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